NodeObfuscator.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  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. * Replaces all identifiers names in specified node with new random names and stores that names in given `namesMap`.
  44. * Reserved names will be ignored.
  45. *
  46. * @param node
  47. * @param namesMap
  48. * @returns {estraverse.VisitorOption}
  49. */
  50. protected replaceAndStoreIdentifiersNames (node: INode, namesMap: Map <string, string>): estraverse.VisitorOption {
  51. if (Nodes.isIdentifierNode(node) && !this.isReservedName(node.name)) {
  52. namesMap.set(node.name, Utils.getRandomVariableName());
  53. node.name = namesMap.get(node.name);
  54. return;
  55. }
  56. return estraverse.VisitorOption.Skip;
  57. }
  58. /**
  59. * @param node
  60. * @param parentNode
  61. * @param namesMap
  62. */
  63. protected replaceNodeIdentifierByNewValue (node: INode, parentNode: INode, namesMap: Map <string, string>): void {
  64. if (Nodes.isIdentifierNode(node) && namesMap.has(node.name)) {
  65. const parentNodeIsPropertyNode: boolean = (
  66. Nodes.isPropertyNode(parentNode) &&
  67. parentNode.key === node
  68. ),
  69. parentNodeIsMemberExpressionNode: boolean = (
  70. Nodes.isMemberExpressionNode(parentNode) &&
  71. parentNode.computed === false &&
  72. parentNode.property === node
  73. );
  74. if (parentNodeIsPropertyNode || parentNodeIsMemberExpressionNode) {
  75. return;
  76. }
  77. node.name = namesMap.get(node.name);
  78. }
  79. }
  80. /**
  81. * @param nodeValue
  82. * @returns {string}
  83. */
  84. protected replaceLiteralBooleanByJSFuck (nodeValue: boolean): string {
  85. return nodeValue ? JSFuck.True : JSFuck.False;
  86. }
  87. /**
  88. * @param nodeValue
  89. * @returns {string}
  90. */
  91. protected replaceLiteralNumberByHexadecimalValue (nodeValue: number): string {
  92. const prefix: string = '0x';
  93. if (!Utils.isInteger(nodeValue)) {
  94. return String(nodeValue);
  95. }
  96. return `${prefix}${Utils.decToHex(nodeValue)}`;
  97. }
  98. /**
  99. * @param nodeValue
  100. * @returns {string}
  101. */
  102. protected replaceLiteralValueByUnicodeValue (nodeValue: string): string {
  103. let value: string = nodeValue,
  104. replaceByUnicodeArrayFlag: boolean = Math.random() <= this.options.get('unicodeArrayThreshold');
  105. if (this.options.get('encodeUnicodeLiterals') && replaceByUnicodeArrayFlag) {
  106. value = Utils.btoa(value);
  107. }
  108. value = Utils.stringToUnicode(value);
  109. if (!this.options.get('unicodeArray') || !replaceByUnicodeArrayFlag) {
  110. return value;
  111. }
  112. return this.replaceLiteralValueByUnicodeArrayCall(value);
  113. }
  114. /**
  115. * @param value
  116. * @returns {string}
  117. */
  118. protected replaceLiteralValueByUnicodeArrayCall (value: string): string {
  119. let unicodeArrayNode: UnicodeArrayNode = <UnicodeArrayNode> this.nodes.get('unicodeArrayNode'),
  120. unicodeArray: string[] = unicodeArrayNode.getNodeData(),
  121. sameIndex: number = unicodeArray.indexOf(value),
  122. index: number,
  123. hexadecimalIndex: string;
  124. if (sameIndex >= 0) {
  125. index = sameIndex;
  126. } else {
  127. index = unicodeArray.length;
  128. unicodeArrayNode.updateNodeData(value);
  129. }
  130. hexadecimalIndex = this.replaceLiteralNumberByHexadecimalValue(index);
  131. if (this.options.get('wrapUnicodeArrayCalls')) {
  132. return `${this.nodes.get('unicodeArrayCallsWrapper').getNodeIdentifier()}('${hexadecimalIndex}')`;
  133. }
  134. return `${unicodeArrayNode.getNodeIdentifier()}[${hexadecimalIndex}]`;
  135. }
  136. }