LogicalExpressionControlFlowReplacer.spec.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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('LogicalExpressionControlFlowReplacer', function () {
  6. this.timeout(100000);
  7. describe('replace', () => {
  8. const variableMatch: string = '_0x([a-f0-9]){4,6}';
  9. describe('Variant #1 - single logical expression', () => {
  10. const controlFlowStorageCallRegExp: RegExp = new RegExp(
  11. `var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\]\\(!!\\[\\], *!\\[\\]\\);`
  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 logical expression node with call to control flow storage node', () => {
  26. assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
  27. });
  28. });
  29. describe('Variant #2 - multiple logical 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}'\\])\\(!!\\[\\], *!\\[\\]\\);`
  36. );
  37. const controlFlowStorageCallRegExp2: RegExp = new RegExp(
  38. `var _0x(?:[a-f0-9]){4,6} *= *(${variableMatch}\\['\\w{5}'\\])\\(!\\[\\], *!!\\[\\]\\);`
  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 logical 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 - single logical expression with unary expression', () => {
  81. const controlFlowStorageCallRegExp: RegExp = new RegExp(
  82. `var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\]\\(!${variableMatch}, *!${variableMatch}\\);`
  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('should replace logical unary expression with call to control flow storage node', () => {
  97. assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
  98. });
  99. });
  100. describe('prohibited nodes Variant #1', () => {
  101. const regExp: RegExp = new RegExp(
  102. `var ${variableMatch} *= *${variableMatch}\\[${variableMatch}\\] *&& *!\\[\\];`
  103. );
  104. let obfuscatedCode: string;
  105. before(() => {
  106. const code: string = readFileAsString(__dirname + '/fixtures/prohibited-nodes.js');
  107. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  108. code,
  109. {
  110. ...NO_ADDITIONAL_NODES_PRESET,
  111. controlFlowFlattening: true,
  112. controlFlowFlatteningThreshold: .1
  113. }
  114. ).getObfuscatedCode();
  115. });
  116. it('shouldn\'t replace prohibited expression nodes', () => {
  117. assert.match(obfuscatedCode, regExp);
  118. });
  119. });
  120. });
  121. });