CallExpressionControlFlowReplacer.spec.ts 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import { assert } from 'chai';
  2. import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/presets/NoCustomNodes';
  3. import { readFileAsString } from '../../../../../helpers/readFileAsString';
  4. import { JavaScriptObfuscator } from '../../../../../../src/JavaScriptObfuscatorFacade';
  5. describe('CallExpressionControlFlowReplacer', function () {
  6. this.timeout(100000);
  7. describe('replace', () => {
  8. const variableMatch: string = '_0x([a-f0-9]){4,6}';
  9. describe('Variant #1 - single call expression', () => {
  10. const controlFlowStorageCallRegExp: RegExp = new RegExp(
  11. `var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\]\\(${variableMatch}, *0x1, *0x2\\);`
  12. );
  13. let obfuscatedCode: string;
  14. before(() => {
  15. const code: string = readFileAsString(__dirname + '/fixtures/input-1.js');
  16. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  17. code,
  18. {
  19. ...NO_ADDITIONAL_NODES_PRESET,
  20. controlFlowFlattening: true,
  21. controlFlowFlatteningThreshold: 1
  22. }
  23. ).getObfuscatedCode();
  24. });
  25. it('should replace call expression node with call to control flow storage node', () => {
  26. assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
  27. });
  28. });
  29. describe('Variant #2 - multiple call expressions with threshold = 1', () => {
  30. const expectedMatchErrorsCount: number = 0;
  31. const expectedChance: number = 0.5;
  32. const samplesCount: number = 1000;
  33. const delta: number = 0.1;
  34. const controlFlowStorageCallRegExp1: RegExp = new RegExp(
  35. `var _0x(?:[a-f0-9]){4,6} *= *(${variableMatch}\\['\\w{5}'\\])\\(${variableMatch}, *0x1, *0x2\\);`
  36. );
  37. const controlFlowStorageCallRegExp2: RegExp = new RegExp(
  38. `var _0x(?:[a-f0-9]){4,6} *= *(${variableMatch}\\['\\w{5}'\\])\\(${variableMatch}, *0x2, *0x3\\);`
  39. );
  40. let matchErrorsCount: number = 0,
  41. usingExistingIdentifierChance: number;
  42. before(() => {
  43. const code: string = readFileAsString(__dirname + '/fixtures/input-2.js');
  44. let obfuscatedCode: string,
  45. firstMatchArray: RegExpMatchArray | null,
  46. secondMatchArray: RegExpMatchArray | null,
  47. firstMatch: string | undefined,
  48. secondMatch: string | undefined,
  49. equalsValue: number = 0;
  50. for (let i = 0; i < samplesCount; i++) {
  51. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  52. code,
  53. {
  54. ...NO_ADDITIONAL_NODES_PRESET,
  55. controlFlowFlattening: true,
  56. controlFlowFlatteningThreshold: 1
  57. }
  58. ).getObfuscatedCode();
  59. firstMatchArray = obfuscatedCode.match(controlFlowStorageCallRegExp1);
  60. secondMatchArray = obfuscatedCode.match(controlFlowStorageCallRegExp2);
  61. if (!firstMatchArray || !secondMatchArray) {
  62. matchErrorsCount++;
  63. continue;
  64. }
  65. firstMatch = firstMatchArray ? firstMatchArray[1] : undefined;
  66. secondMatch = secondMatchArray ? secondMatchArray[1] : undefined;
  67. if (firstMatch === secondMatch) {
  68. equalsValue++;
  69. }
  70. }
  71. usingExistingIdentifierChance = equalsValue / samplesCount;
  72. });
  73. it('should replace call expression node by call to control flow storage node', () => {
  74. assert.equal(matchErrorsCount, expectedMatchErrorsCount);
  75. });
  76. it('should use existing identifier for control flow storage with expected chance', () => {
  77. assert.closeTo(usingExistingIdentifierChance, expectedChance, delta);
  78. });
  79. });
  80. describe('Variant #3 - call expression callee is member expression node', () => {
  81. const regExp: RegExp = new RegExp(
  82. `var ${variableMatch} *= *${variableMatch}\\['sum'\\]\\(0x1, *0x2\\);`
  83. );
  84. let obfuscatedCode: string;
  85. before(() => {
  86. const code: string = readFileAsString(__dirname + '/fixtures/input-3.js');
  87. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  88. code,
  89. {
  90. ...NO_ADDITIONAL_NODES_PRESET,
  91. controlFlowFlattening: true,
  92. controlFlowFlatteningThreshold: 1
  93. }
  94. ).getObfuscatedCode();
  95. });
  96. it('shouldn\'t replace call expression node with call to control flow storage node', () => {
  97. assert.match(obfuscatedCode, regExp);
  98. });
  99. });
  100. describe('Variant #4 - rest as start call argument', () => {
  101. const controlFlowStorageCallRegExp: RegExp = new RegExp(
  102. `${variableMatch}\\['\\w{5}']\\(${variableMatch}, *\\.\\.\\.${variableMatch}, *${variableMatch}\\);`
  103. );
  104. const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
  105. `'\\w{5}' *: *function *\\(${variableMatch}, *\.\.\.${variableMatch}\\) *\\{` +
  106. `return *${variableMatch}\\(\.\.\.${variableMatch}\\);` +
  107. `\\}` +
  108. ``);
  109. let obfuscatedCode: string;
  110. before(() => {
  111. const code: string = readFileAsString(__dirname + '/fixtures/rest-as-start-call-argument.js');
  112. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  113. code,
  114. {
  115. ...NO_ADDITIONAL_NODES_PRESET,
  116. controlFlowFlattening: true,
  117. controlFlowFlatteningThreshold: 1
  118. }
  119. ).getObfuscatedCode();
  120. });
  121. it('should replace call expression node with call to control flow storage node', () => {
  122. assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
  123. });
  124. it('should keep rest parameter and rest call argument, but remove all function parameters after rest parameter', () => {
  125. assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
  126. });
  127. });
  128. describe('Variant #5 - rest as middle call argument', () => {
  129. const controlFlowStorageCallRegExp: RegExp = new RegExp(
  130. `${variableMatch}\\['\\w{5}']\\(${variableMatch}, *${variableMatch}, *\\.\\.\\.${variableMatch}, *${variableMatch}\\);`
  131. );
  132. const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
  133. `'\\w{5}' *: *function *\\(${variableMatch}, *${variableMatch}, *\.\.\.${variableMatch}\\) *\\{` +
  134. `return *${variableMatch}\\(${variableMatch}, *\.\.\.${variableMatch}\\);` +
  135. `\\}` +
  136. ``);
  137. let obfuscatedCode: string;
  138. before(() => {
  139. const code: string = readFileAsString(__dirname + '/fixtures/rest-as-middle-call-argument.js');
  140. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  141. code,
  142. {
  143. ...NO_ADDITIONAL_NODES_PRESET,
  144. controlFlowFlattening: true,
  145. controlFlowFlatteningThreshold: 1
  146. }
  147. ).getObfuscatedCode();
  148. });
  149. it('should replace call expression node with call to control flow storage node', () => {
  150. assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
  151. });
  152. it('should keep rest parameter and rest call argument, but remove all function parameters after rest parameter', () => {
  153. assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
  154. });
  155. });
  156. describe('Variant #6 - rest as last call argument', () => {
  157. const controlFlowStorageCallRegExp: RegExp = new RegExp(
  158. `${variableMatch}\\['\\w{5}']\\(${variableMatch}, *${variableMatch}, *\\.\\.\\.${variableMatch}\\);`
  159. );
  160. const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
  161. `'\\w{5}' *: *function *\\(${variableMatch}, *${variableMatch}, *\.\.\.${variableMatch}\\) *\\{` +
  162. `return *${variableMatch}\\(${variableMatch}, *\.\.\.${variableMatch}\\);` +
  163. `\\}` +
  164. ``);
  165. let obfuscatedCode: string;
  166. before(() => {
  167. const code: string = readFileAsString(__dirname + '/fixtures/rest-as-last-call-argument.js');
  168. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  169. code,
  170. {
  171. ...NO_ADDITIONAL_NODES_PRESET,
  172. controlFlowFlattening: true,
  173. controlFlowFlatteningThreshold: 1
  174. }
  175. ).getObfuscatedCode();
  176. });
  177. it('should replace call expression node with call to control flow storage node', () => {
  178. assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
  179. });
  180. it('should keep rest parameter and rest call argument inside control flow storage node function', () => {
  181. assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
  182. });
  183. });
  184. });
  185. });