NodeObfuscator.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import * as estraverse from 'estraverse';
  2. import { ICustomNode } from '../interfaces/ICustomNode';
  3. import { INodeObfuscator } from '../interfaces/INodeObfuscator';
  4. import { INode } from "../interfaces/nodes/INode";
  5. import { IOptions } from "../interfaces/IOptions";
  6. import { JSFuck } from "../enums/JSFuck";
  7. import { Nodes } from "../Nodes";
  8. import { UnicodeArrayNode } from "../custom-nodes/unicode-array-nodes/UnicodeArrayNode";
  9. import { Utils } from '../Utils';
  10. export abstract class NodeObfuscator implements INodeObfuscator {
  11. /**
  12. * @type Map <string, Node>
  13. */
  14. protected nodes: Map <string, ICustomNode>;
  15. /**
  16. * @type {IOptions}
  17. */
  18. protected options: IOptions;
  19. /**
  20. * @param nodes
  21. * @param options
  22. */
  23. constructor(nodes: Map <string, ICustomNode>, options: IOptions) {
  24. this.nodes = nodes;
  25. this.options = options;
  26. }
  27. /**
  28. * @param node
  29. * @param parentNode
  30. */
  31. public abstract obfuscateNode (node: INode, parentNode?: INode): void;
  32. /**
  33. * @param name
  34. * @returns {boolean}
  35. */
  36. protected isReservedName (name: string): boolean {
  37. return this.options.get<string[]>('reservedNames')
  38. .some((reservedName: string) => {
  39. return new RegExp(reservedName, 'g').test(name);
  40. });
  41. }
  42. /**
  43. * Store all identifiers names as keys in given `namesMap` with random names as value.
  44. * Reserved names will be ignored.
  45. *
  46. * @param node
  47. * @param namesMap
  48. * @returns {estraverse.VisitorOption}
  49. */
  50. protected storeIdentifiersNames (
  51. node: INode,
  52. namesMap: Map <string, string>
  53. ): estraverse.VisitorOption {
  54. if (Nodes.isIdentifierNode(node) && !this.isReservedName(node.name)) {
  55. namesMap.set(node.name, Utils.getRandomVariableName());
  56. return;
  57. }
  58. return estraverse.VisitorOption.Skip;
  59. }
  60. /**
  61. * @param node
  62. * @param parentNode
  63. * @param namesMap
  64. */
  65. protected replaceIdentifiersWithRandomNames (
  66. node: INode,
  67. parentNode: INode,
  68. namesMap: Map <string, string>
  69. ): void {
  70. if (Nodes.isIdentifierNode(node) && namesMap.has(node.name)) {
  71. const parentNodeIsPropertyNode: boolean = (
  72. Nodes.isPropertyNode(parentNode) &&
  73. parentNode.key === node
  74. ),
  75. parentNodeIsMemberExpressionNode: boolean = (
  76. Nodes.isMemberExpressionNode(parentNode) &&
  77. parentNode.computed === false &&
  78. parentNode.property === node
  79. );
  80. if (parentNodeIsPropertyNode || parentNodeIsMemberExpressionNode) {
  81. return;
  82. }
  83. node.name = namesMap.get(node.name);
  84. }
  85. }
  86. /**
  87. * @param nodeValue
  88. * @returns {string}
  89. */
  90. protected replaceLiteralBooleanWithJSFuck (nodeValue: boolean): string {
  91. return nodeValue ? JSFuck.True : JSFuck.False;
  92. }
  93. /**
  94. * @param nodeValue
  95. * @returns {string}
  96. */
  97. protected replaceLiteralNumberWithHexadecimalValue (nodeValue: number): string {
  98. const prefix: string = '0x';
  99. if (!Utils.isInteger(nodeValue)) {
  100. return String(nodeValue);
  101. }
  102. return `${prefix}${Utils.decToHex(nodeValue)}`;
  103. }
  104. /**
  105. * @param nodeValue
  106. * @returns {string}
  107. */
  108. protected replaceLiteralValueWithUnicodeValue (nodeValue: string): string {
  109. let value: string = nodeValue,
  110. replaceWithUnicodeArrayFlag: boolean = Math.random() <= this.options.get('unicodeArrayThreshold');
  111. if (this.options.get('encodeUnicodeLiterals') && replaceWithUnicodeArrayFlag) {
  112. value = Utils.btoa(value);
  113. }
  114. value = Utils.stringToUnicode(value);
  115. if (!this.options.get('unicodeArray') || !replaceWithUnicodeArrayFlag) {
  116. return value;
  117. }
  118. return this.replaceLiteralValueWithUnicodeArrayCall(value);
  119. }
  120. /**
  121. * @param value
  122. * @returns {string}
  123. */
  124. protected replaceLiteralValueWithUnicodeArrayCall (value: string): string {
  125. let unicodeArrayNode: UnicodeArrayNode = <UnicodeArrayNode> this.nodes.get('unicodeArrayNode'),
  126. unicodeArray: string[] = unicodeArrayNode.getNodeData(),
  127. sameIndex: number = unicodeArray.indexOf(value),
  128. index: number,
  129. hexadecimalIndex: string;
  130. if (sameIndex >= 0) {
  131. index = sameIndex;
  132. } else {
  133. index = unicodeArray.length;
  134. unicodeArrayNode.updateNodeData(value);
  135. }
  136. hexadecimalIndex = this.replaceLiteralNumberWithHexadecimalValue(index);
  137. if (this.options.get('wrapUnicodeArrayCalls')) {
  138. return `${this.nodes.get('unicodeArrayCallsWrapper').getNodeIdentifier()}('${hexadecimalIndex}')`;
  139. }
  140. return `${unicodeArrayNode.getNodeIdentifier()}[${hexadecimalIndex}]`;
  141. }
  142. }