StringLiteralObfuscatingReplacer.ts 8.0 KB

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