StringLiteralReplacer.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import { injectable, inject } from 'inversify';
  2. import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
  3. import * as ESTree from 'estree';
  4. import { ICustomNodeGroup } from '../../../interfaces/custom-nodes/ICustomNodeGroup';
  5. import { IEncodedValue } from '../../../interfaces/node-transformers/obfuscating-transformers/IEncodedValue';
  6. import { IOptions } from '../../../interfaces/options/IOptions';
  7. import { IStorage } from '../../../interfaces/storages/IStorage';
  8. import { StringArrayEncoding } from '../../../enums/StringArrayEncoding';
  9. import { AbstractObfuscatingReplacer } from './AbstractObfuscatingReplacer';
  10. import { CryptUtils } from '../../../utils/CryptUtils';
  11. import { Nodes } from '../../../node/Nodes';
  12. import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
  13. import { Utils } from '../../../utils/Utils';
  14. @injectable()
  15. export class StringLiteralReplacer extends AbstractObfuscatingReplacer {
  16. /**
  17. * @type {number}
  18. */
  19. private static readonly minimumLengthForStringArray: number = 3;
  20. /**
  21. * @type {IStorage<ICustomNodeGroup>}
  22. */
  23. private readonly customNodeGroupStorage: IStorage<ICustomNodeGroup>;
  24. /**
  25. * @type {Map<string, ESTree.Node>}
  26. */
  27. private readonly nodesCache: Map <string, ESTree.Node> = new Map();
  28. /**
  29. * @type {string[]}
  30. */
  31. private readonly rc4Keys: string[];
  32. /**
  33. * @type {Map<string, string>}
  34. */
  35. private readonly stringLiteralHexadecimalIndexCache: Map <string, string> = new Map();
  36. /**
  37. * @type {IStorage<string>}
  38. */
  39. private readonly stringArrayStorage: IStorage<string>;
  40. /**
  41. * @param customNodeGroupStorage
  42. * @param stringArrayStorage
  43. * @param options
  44. */
  45. constructor (
  46. @inject(ServiceIdentifiers.TCustomNodeGroupStorage) customNodeGroupStorage: IStorage<ICustomNodeGroup>,
  47. @inject(ServiceIdentifiers.TStringArrayStorage) stringArrayStorage: IStorage<string>,
  48. @inject(ServiceIdentifiers.IOptions) options: IOptions
  49. ) {
  50. super(options);
  51. this.customNodeGroupStorage = customNodeGroupStorage;
  52. this.stringArrayStorage = stringArrayStorage;
  53. this.rc4Keys = RandomGeneratorUtils.getRandomGenerator()
  54. .n(() => RandomGeneratorUtils.getRandomGenerator().string({length: 4}), 50);
  55. }
  56. /**
  57. * @param hexadecimalIndex
  58. * @return {ESTree.Literal}
  59. */
  60. private static getHexadecimalLiteralNode (hexadecimalIndex: string): ESTree.Literal {
  61. const hexadecimalLiteralNode: ESTree.Literal = Nodes.getLiteralNode(hexadecimalIndex);
  62. hexadecimalLiteralNode.obfuscatedNode = true;
  63. return hexadecimalLiteralNode;
  64. }
  65. /**
  66. * @param literalValue
  67. * @return {ESTree.Literal}
  68. */
  69. private static getRc4KeyLiteralNode (literalValue: string): ESTree.Literal {
  70. const rc4KeyLiteralNode: ESTree.Literal = Nodes.getLiteralNode(literalValue);
  71. rc4KeyLiteralNode.obfuscatedNode = true;
  72. return rc4KeyLiteralNode;
  73. }
  74. /**
  75. * @param nodeValue
  76. * @returns {ESTree.Node}
  77. */
  78. public replace (nodeValue: string): ESTree.Node {
  79. const usingStringArray: boolean = this.canUseStringArray(nodeValue);
  80. const cacheKey: string = `${nodeValue}-${String(usingStringArray)}`;
  81. if (this.nodesCache.has(cacheKey) && this.options.stringArrayEncoding !== StringArrayEncoding.rc4) {
  82. return <ESTree.Node>this.nodesCache.get(cacheKey);
  83. }
  84. let resultNode: ESTree.Node;
  85. if (usingStringArray) {
  86. resultNode = this.replaceWithStringArrayCallNode(nodeValue);
  87. } else {
  88. resultNode = Nodes.getLiteralNode(
  89. `${Utils.stringToUnicodeEscapeSequence(nodeValue, !this.options.unicodeEscapeSequence)}`
  90. );
  91. }
  92. this.nodesCache.set(cacheKey, resultNode);
  93. return resultNode;
  94. }
  95. /**
  96. * @param nodeValue
  97. * @return {boolean}
  98. */
  99. private canUseStringArray (nodeValue: string): boolean {
  100. return (
  101. this.options.stringArray &&
  102. nodeValue.length >= StringLiteralReplacer.minimumLengthForStringArray &&
  103. RandomGeneratorUtils.getMathRandom() <= this.options.stringArrayThreshold
  104. );
  105. }
  106. /**
  107. * @param value
  108. * @return {string}
  109. */
  110. private getArrayHexadecimalIndex (value: string): string {
  111. if (this.stringLiteralHexadecimalIndexCache.has(value)) {
  112. return <string>this.stringLiteralHexadecimalIndexCache.get(value);
  113. }
  114. const stringArrayStorageLength: number = this.stringArrayStorage.getLength();
  115. const hexadecimalIndex: string = `${Utils.hexadecimalPrefix}${Utils.decToHex(stringArrayStorageLength)}`;
  116. this.stringArrayStorage.set(stringArrayStorageLength, value);
  117. this.stringLiteralHexadecimalIndexCache.set(value, hexadecimalIndex);
  118. return hexadecimalIndex;
  119. }
  120. /**
  121. * @param value
  122. * @returns {IEncodedValue}
  123. */
  124. private getEncodedValue (value: string): IEncodedValue {
  125. let encodedValue: string,
  126. key: string | undefined;
  127. switch (this.options.stringArrayEncoding) {
  128. case StringArrayEncoding.rc4:
  129. key = RandomGeneratorUtils.getRandomGenerator().pickone(this.rc4Keys);
  130. encodedValue = CryptUtils.btoa(CryptUtils.rc4(value, key));
  131. break;
  132. case StringArrayEncoding.base64:
  133. encodedValue = CryptUtils.btoa(value);
  134. break;
  135. default:
  136. encodedValue = value;
  137. }
  138. encodedValue = Utils.stringToUnicodeEscapeSequence(encodedValue, !this.options.unicodeEscapeSequence);
  139. return { encodedValue, key };
  140. }
  141. /**
  142. * @param value
  143. * @returns {ESTree.Node}
  144. */
  145. private replaceWithStringArrayCallNode (value: string): ESTree.Node {
  146. const { encodedValue, key }: IEncodedValue = this.getEncodedValue(value);
  147. const rotatedStringArrayStorageId: string = Utils.stringRotate(this.stringArrayStorage.getStorageId(), 1);
  148. const stringArrayStorageCallsWrapperName: string = `_${Utils.hexadecimalPrefix}${rotatedStringArrayStorageId}`;
  149. const callExpressionArgs: (ESTree.Expression | ESTree.SpreadElement)[] = [
  150. StringLiteralReplacer.getHexadecimalLiteralNode(
  151. this.getArrayHexadecimalIndex(encodedValue)
  152. )
  153. ];
  154. if (key) {
  155. callExpressionArgs.push(StringLiteralReplacer.getRc4KeyLiteralNode(
  156. Utils.stringToUnicodeEscapeSequence(key, !this.options.unicodeEscapeSequence)
  157. ));
  158. }
  159. return Nodes.getCallExpressionNode(
  160. Nodes.getIdentifierNode(stringArrayStorageCallsWrapperName),
  161. callExpressionArgs
  162. );
  163. }
  164. }