BlockStatementControlFlowTransformer.spec.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import { assert } from 'chai';
  2. import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
  3. import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
  4. import { readFileAsString } from '../../../helpers/readFileAsString';
  5. import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
  6. describe('BlockStatementControlFlowTransformer', () => {
  7. describe('transformNode (blockStatementNode: ESTree.BlockStatement): ESTree.Node', () => {
  8. describe('variant #1: 5 simple statements', () => {
  9. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  10. readFileAsString(
  11. './test/fixtures/node-transformers/node-control-flow-transformers/block-statement-control-flow-transformer-1.js'
  12. ),
  13. {
  14. ...NO_CUSTOM_NODES_PRESET,
  15. controlFlowFlattening: true,
  16. controlFlowFlatteningThreshold: 1,
  17. unicodeEscapeSequence: false
  18. }
  19. );
  20. const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
  21. const statementRegExp1: RegExp = /console\['log'\]\(0x1\);/;
  22. const statementRegExp2: RegExp = /console\['log'\]\(0x2\);/;
  23. const statementRegExp3: RegExp = /console\['log'\]\(0x3\);/;
  24. const statementRegExp4: RegExp = /console\['log'\]\(0x4\);/;
  25. const statementRegExp5: RegExp = /console\['log'\]\(0x5\);/;
  26. const switchCaseRegExp: RegExp = /switch *\(_0x([a-z0-9]){4,6}\[_0x([a-z0-9]){4,6}\+\+\]\) *\{/;
  27. const switchCaseLengthRegExp: RegExp = /case *'[0-5]': *console\['log'\]\(0x[0-6]\);/g;
  28. const switchCaseLength: number = obfuscatedCode.match(switchCaseLengthRegExp)!.length;
  29. const switchCaseMapRegExp: RegExp = /var *_0x(?:[a-z0-9]){4,6} *= *'(.*?)'/;
  30. const switchCaseMapMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(switchCaseMapRegExp);
  31. const switchCaseMapMatch: string = switchCaseMapMatches[1];
  32. const switchCaseMap: string[] = switchCaseMapMatch.replace(/\\x7c/g, '|').split('|').sort();
  33. it('should save all statements', () => {
  34. assert.match(obfuscatedCode, statementRegExp1);
  35. assert.match(obfuscatedCode, statementRegExp2);
  36. assert.match(obfuscatedCode, statementRegExp3);
  37. assert.match(obfuscatedCode, statementRegExp4);
  38. assert.match(obfuscatedCode, statementRegExp5);
  39. });
  40. it('should wrap block statement statements in switch case structure', () => {
  41. assert.match(obfuscatedCode, switchCaseRegExp);
  42. assert.equal(switchCaseLength, 5);
  43. });
  44. it('should create variable with order of switch cases sequence', () => {
  45. assert.deepEqual(switchCaseMap, ['0', '1', '2', '3', '4']);
  46. });
  47. });
  48. describe('variant #2: 5 simple statements inside while loop without break or continue statement', () => {
  49. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  50. readFileAsString(
  51. './test/fixtures/node-transformers/node-control-flow-transformers/block-statement-control-flow-transformer-2.js'
  52. ),
  53. {
  54. ...NO_CUSTOM_NODES_PRESET,
  55. controlFlowFlattening: true,
  56. controlFlowFlatteningThreshold: 1,
  57. unicodeEscapeSequence: false
  58. }
  59. );
  60. const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
  61. const statementRegExp1: RegExp = /console\['log'\]\(0x1\);/;
  62. const statementRegExp2: RegExp = /console\['log'\]\(0x2\);/;
  63. const statementRegExp3: RegExp = /console\['log'\]\(0x3\);/;
  64. const statementRegExp4: RegExp = /console\['log'\]\(0x4\);/;
  65. const statementRegExp5: RegExp = /console\['log'\]\(0x5\);/;
  66. const switchCaseRegExp: RegExp = /switch *\(_0x([a-z0-9]){4,6}\[_0x([a-z0-9]){4,6}\+\+\]\) *\{/;
  67. const switchCaseLengthRegExp: RegExp = /case *'[0-5]': *console\['log'\]\(0x[0-6]\);/g;
  68. const switchCaseLength: number = obfuscatedCode.match(switchCaseLengthRegExp)!.length;
  69. const switchCaseMapRegExp: RegExp = /var *_0x(?:[a-z0-9]){4,6} *= *'(.*?)'/;
  70. const switchCaseMapMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(switchCaseMapRegExp);
  71. const switchCaseMapMatch: string = switchCaseMapMatches[1];
  72. const switchCaseMap: string[] = switchCaseMapMatch.replace(/\\x7c/g, '|').split('|').sort();
  73. it('should save all statements', () => {
  74. assert.match(obfuscatedCode, statementRegExp1);
  75. assert.match(obfuscatedCode, statementRegExp2);
  76. assert.match(obfuscatedCode, statementRegExp3);
  77. assert.match(obfuscatedCode, statementRegExp4);
  78. assert.match(obfuscatedCode, statementRegExp5);
  79. });
  80. it('should wrap block statement statements in switch case structure', () => {
  81. assert.match(obfuscatedCode, switchCaseRegExp);
  82. assert.equal(switchCaseLength, 5);
  83. });
  84. it('should create variable with order of switch cases sequence', () => {
  85. assert.deepEqual(switchCaseMap, ['0', '1', '2', '3', '4']);
  86. });
  87. });
  88. describe('variant #3: less then 5 statements', () => {
  89. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  90. readFileAsString(
  91. './test/fixtures/node-transformers/node-control-flow-transformers/block-statement-control-flow-transformer-one-statement.js'
  92. ),
  93. {
  94. ...NO_CUSTOM_NODES_PRESET,
  95. controlFlowFlattening: true,
  96. controlFlowFlatteningThreshold: 1
  97. }
  98. );
  99. const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
  100. const statementRegExp: RegExp = /^\(function *\( *\) *\{ *console\['(\\x[a-f0-9]*){3}'\]\(0x1\); *\} *\( *\) *\);$/;
  101. it('shouldn\'t transform block statement if statements length less than 5', () => {
  102. assert.match(obfuscatedCode, statementRegExp);
  103. });
  104. });
  105. describe('variant #4: const declaration inside block statement', () => {
  106. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  107. readFileAsString(
  108. './test/fixtures/node-transformers/node-control-flow-transformers/block-statement-control-flow-transformer-const-declaration.js'
  109. ),
  110. {
  111. ...NO_CUSTOM_NODES_PRESET,
  112. controlFlowFlattening: true,
  113. controlFlowFlatteningThreshold: 1
  114. }
  115. );
  116. const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
  117. const statementRegExp: RegExp = /^\(function *\( *\) *\{ *const *_0x([a-z0-9]){4,6} *= *0x1; *console\['(\\x[a-f0-9]*){3}'\]\(0x1\);/;
  118. it('shouldn\'t transform block statement if block statement contain variable declaration with `const` kind', () => {
  119. assert.match(obfuscatedCode, statementRegExp);
  120. });
  121. });
  122. describe('variant #5: let declaration inside block statement', () => {
  123. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  124. readFileAsString(
  125. './test/fixtures/node-transformers/node-control-flow-transformers/block-statement-control-flow-transformer-let-declaration.js'
  126. ),
  127. {
  128. ...NO_CUSTOM_NODES_PRESET,
  129. controlFlowFlattening: true,
  130. controlFlowFlatteningThreshold: 1
  131. }
  132. );
  133. const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
  134. const statementRegExp: RegExp = /^\(function *\( *\) *\{ *let *_0x([a-z0-9]){4,6} *= *0x1; *console\['(\\x[a-f0-9]*){3}'\]\(0x1\);/;
  135. it('shouldn\'t transform block statement if block statement contain variable declaration with `let` kind', () => {
  136. assert.match(obfuscatedCode, statementRegExp);
  137. });
  138. });
  139. describe('variant #6: break statement inside block statement', () => {
  140. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  141. readFileAsString(
  142. './test/fixtures/node-transformers/node-control-flow-transformers/block-statement-control-flow-transformer-break-statement.js'
  143. ),
  144. {
  145. ...NO_CUSTOM_NODES_PRESET,
  146. controlFlowFlattening: true,
  147. controlFlowFlatteningThreshold: 1
  148. }
  149. );
  150. const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
  151. const statementRegExp: RegExp = /^\(function *\( *\) *\{ *while *\(!!\[\]\) *\{ *break; *console\['(\\x[a-f0-9]*){3}'\]\(0x1\);/;
  152. it('shouldn\'t transform block statement if block statement contain break statement', () => {
  153. assert.match(obfuscatedCode, statementRegExp);
  154. });
  155. });
  156. describe('variant #7: continue statement inside block statement', () => {
  157. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  158. readFileAsString(
  159. './test/fixtures/node-transformers/node-control-flow-transformers/block-statement-control-flow-transformer-continue-statement.js'
  160. ),
  161. {
  162. ...NO_CUSTOM_NODES_PRESET,
  163. controlFlowFlattening: true,
  164. controlFlowFlatteningThreshold: 1
  165. }
  166. );
  167. const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
  168. const statementRegExp: RegExp = /^\(function *\( *\) *\{ *while *\(!!\[\]\) *\{ *continue; *console\['(\\x[a-f0-9]*){3}'\]\(0x1\);/;
  169. it('shouldn\'t transform block statement if block statement contain continue statement', () => {
  170. assert.match(obfuscatedCode, statementRegExp);
  171. });
  172. });
  173. describe('variant #8: function declaration inside block statement', () => {
  174. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  175. readFileAsString(
  176. './test/fixtures/node-transformers/node-control-flow-transformers/block-statement-control-flow-transformer-function-declaration.js'
  177. ),
  178. {
  179. ...NO_CUSTOM_NODES_PRESET,
  180. controlFlowFlattening: true,
  181. controlFlowFlatteningThreshold: 1
  182. }
  183. );
  184. const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
  185. const statementRegExp: RegExp = /^\(function *\( *\) *\{ *function *_0x([a-z0-9]){4,6} *\( *\) *\{ *\} *console\['(\\x[a-f0-9]*){3}'\]\(0x1\);/;
  186. it('shouldn\'t transform block statement if block statement contain function declaration', () => {
  187. assert.match(obfuscatedCode, statementRegExp);
  188. });
  189. });
  190. describe('variant #9: `controlFlowFlatteningThreshold` chance', () => {
  191. const samples: number = 1000;
  192. const controlFlowFlatteningThreshold: number = 0.5;
  193. const delta: number = 0.1;
  194. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  195. readFileAsString(
  196. './test/fixtures/node-transformers/node-control-flow-transformers/block-statement-control-flow-transformer-1.js'
  197. ).repeat(samples),
  198. {
  199. ...NO_CUSTOM_NODES_PRESET,
  200. controlFlowFlattening: true,
  201. controlFlowFlatteningThreshold: controlFlowFlatteningThreshold,
  202. }
  203. );
  204. const regExp1: RegExp = /switch *\(_0x([a-z0-9]){4,6}\[_0x([a-z0-9]){4,6}\+\+\]\) *\{/g;
  205. const regExp2: RegExp = /\(function *\( *\) *\{ *console\['(\\x[a-f0-9]*){3}'\]\(0x1\);/g;
  206. const transformedStatementMatchesLength = obfuscationResult.getObfuscatedCode().match(regExp1)!.length;
  207. const untouchedStatementMatchesLength = obfuscationResult.getObfuscatedCode().match(regExp2)!.length;
  208. it('should transform block statement with `controlFlowFlatteningThreshold` chance', () => {
  209. assert.closeTo(transformedStatementMatchesLength / samples, controlFlowFlatteningThreshold, delta);
  210. assert.closeTo(untouchedStatementMatchesLength / samples, controlFlowFlatteningThreshold, delta);
  211. });
  212. });
  213. });
  214. });