JavaScriptObfuscator.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import { injectable, inject } from 'inversify';
  2. import { ServiceIdentifiers } from './container/ServiceIdentifiers';
  3. import * as esprima from 'esprima';
  4. import * as escodegen from 'escodegen-wallaby';
  5. import * as esmangle from 'esmangle';
  6. import * as ESTree from 'estree';
  7. import { ICustomNodeGroup } from './interfaces/custom-nodes/ICustomNodeGroup';
  8. import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
  9. import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
  10. import { ILogger } from './interfaces/logger/ILogger';
  11. import { IObfuscationEventEmitter } from './interfaces/event-emitters/IObfuscationEventEmitter';
  12. import { IObfuscationResult } from './interfaces/IObfuscationResult';
  13. import { IOptions } from './interfaces/options/IOptions';
  14. import { IRandomGenerator } from './interfaces/utils/IRandomGenerator';
  15. import { ISourceMapCorrector } from './interfaces/source-map/ISourceMapCorrector';
  16. import { IStackTraceAnalyzer } from './interfaces/analyzers/stack-trace-analyzer/IStackTraceAnalyzer';
  17. import { IStackTraceData } from './interfaces/analyzers/stack-trace-analyzer/IStackTraceData';
  18. import { IStorage } from './interfaces/storages/IStorage';
  19. import { ITransformersRunner } from './interfaces/node-transformers/ITransformersRunner';
  20. import { LoggingMessage } from './enums/logger/LoggingMessage';
  21. import { NodeTransformer } from './enums/node-transformers/NodeTransformer';
  22. import { ObfuscationEvent } from './enums/event-emitters/ObfuscationEvent';
  23. import { NodeGuards } from './node/NodeGuards';
  24. @injectable()
  25. export class JavaScriptObfuscator implements IJavaScriptObfuscator {
  26. /**
  27. * @type {GenerateOptions}
  28. */
  29. private static readonly escodegenParams: escodegen.GenerateOptions = {
  30. verbatim: 'x-verbatim-property',
  31. sourceMapWithCode: true
  32. };
  33. /**
  34. * @type {NodeTransformer[]}
  35. */
  36. private static readonly controlFlowTransformersList: NodeTransformer[] = [
  37. NodeTransformer.BlockStatementControlFlowTransformer,
  38. NodeTransformer.FunctionControlFlowTransformer
  39. ];
  40. /**
  41. * @type {NodeTransformer[]}
  42. */
  43. private static readonly convertingTransformersList: NodeTransformer[] = [
  44. NodeTransformer.MemberExpressionTransformer,
  45. NodeTransformer.MethodDefinitionTransformer,
  46. NodeTransformer.TemplateLiteralTransformer
  47. ];
  48. /**
  49. * @type {NodeTransformer[]}
  50. */
  51. private static readonly deadCodeInjectionTransformersList: NodeTransformer[] = [
  52. NodeTransformer.DeadCodeInjectionTransformer
  53. ];
  54. /**
  55. * @type {NodeTransformer[]}
  56. */
  57. private static readonly obfuscatingTransformersList: NodeTransformer[] = [
  58. NodeTransformer.CatchClauseTransformer,
  59. NodeTransformer.ClassDeclarationTransformer,
  60. NodeTransformer.FunctionDeclarationTransformer,
  61. NodeTransformer.FunctionTransformer,
  62. NodeTransformer.LabeledStatementTransformer,
  63. NodeTransformer.LiteralTransformer,
  64. NodeTransformer.ObjectExpressionTransformer,
  65. NodeTransformer.VariableDeclarationTransformer
  66. ];
  67. /**
  68. * @type {NodeTransformer[]}
  69. */
  70. private static readonly preparingTransformersList: NodeTransformer[] = [
  71. NodeTransformer.ObfuscatingGuardsTransformer,
  72. NodeTransformer.ParentificationTransformer
  73. ];
  74. /**
  75. * @type {IStorage<ICustomNodeGroup>}
  76. */
  77. private readonly customNodeGroupStorage: IStorage<ICustomNodeGroup>;
  78. /**
  79. * @type {ILogger}
  80. */
  81. private readonly logger: ILogger;
  82. /**
  83. * @type {IObfuscationEventEmitter}
  84. */
  85. private readonly obfuscationEventEmitter: IObfuscationEventEmitter;
  86. /**
  87. * @type {IOptions}
  88. */
  89. private readonly options: IOptions;
  90. /**
  91. * @type {IRandomGenerator}
  92. */
  93. private readonly randomGenerator: IRandomGenerator;
  94. /**
  95. * @type {ISourceMapCorrector}
  96. */
  97. private readonly sourceMapCorrector: ISourceMapCorrector;
  98. /**
  99. * @type {IStackTraceAnalyzer}
  100. */
  101. private readonly stackTraceAnalyzer: IStackTraceAnalyzer;
  102. /**
  103. * @type {ITransformersRunner}
  104. */
  105. private readonly transformersRunner: ITransformersRunner;
  106. /**
  107. * @param {IStackTraceAnalyzer} stackTraceAnalyzer
  108. * @param {IObfuscationEventEmitter} obfuscationEventEmitter
  109. * @param {IStorage<ICustomNodeGroup>} customNodeGroupStorage
  110. * @param {ITransformersRunner} transformersRunner
  111. * @param {ISourceMapCorrector} sourceMapCorrector
  112. * @param {IRandomGenerator} randomGenerator
  113. * @param {ILogger} logger
  114. * @param {IOptions} options
  115. */
  116. constructor (
  117. @inject(ServiceIdentifiers.IStackTraceAnalyzer) stackTraceAnalyzer: IStackTraceAnalyzer,
  118. @inject(ServiceIdentifiers.IObfuscationEventEmitter) obfuscationEventEmitter: IObfuscationEventEmitter,
  119. @inject(ServiceIdentifiers.TCustomNodeGroupStorage) customNodeGroupStorage: IStorage<ICustomNodeGroup>,
  120. @inject(ServiceIdentifiers.ITransformersRunner) transformersRunner: ITransformersRunner,
  121. @inject(ServiceIdentifiers.ISourceMapCorrector) sourceMapCorrector: ISourceMapCorrector,
  122. @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
  123. @inject(ServiceIdentifiers.ILogger) logger: ILogger,
  124. @inject(ServiceIdentifiers.IOptions) options: IOptions
  125. ) {
  126. this.stackTraceAnalyzer = stackTraceAnalyzer;
  127. this.obfuscationEventEmitter = obfuscationEventEmitter;
  128. this.customNodeGroupStorage = customNodeGroupStorage;
  129. this.transformersRunner = transformersRunner;
  130. this.sourceMapCorrector = sourceMapCorrector;
  131. this.randomGenerator = randomGenerator;
  132. this.logger = logger;
  133. this.options = options;
  134. }
  135. /**
  136. * @param {string} sourceCode
  137. * @returns {IObfuscationResult}
  138. */
  139. public obfuscate (sourceCode: string): IObfuscationResult {
  140. const timeStart: number = Date.now();
  141. this.logger.info(LoggingMessage.ObfuscationStarted);
  142. this.logger.info(LoggingMessage.RandomGeneratorSeed, this.randomGenerator.getSeed());
  143. // parse AST tree
  144. const astTree: ESTree.Program = this.parseCode(sourceCode);
  145. // obfuscate AST tree
  146. const obfuscatedAstTree: ESTree.Program = this.transformAstTree(astTree);
  147. // generate code
  148. const generatorOutput: IGeneratorOutput = this.generateCode(sourceCode, obfuscatedAstTree);
  149. const obfuscationTime: number = (Date.now() - timeStart) / 1000;
  150. this.logger.success(LoggingMessage.ObfuscationCompleted, obfuscationTime);
  151. return this.getObfuscationResult(generatorOutput);
  152. }
  153. /**
  154. * @param {string} sourceCode
  155. * @returns {Program}
  156. */
  157. private parseCode (sourceCode: string): ESTree.Program {
  158. return esprima.parseScript(sourceCode, {
  159. attachComment: true,
  160. loc: this.options.sourceMap
  161. });
  162. }
  163. /**
  164. * @param {Program} astTree
  165. * @returns {Program}
  166. */
  167. private transformAstTree (astTree: ESTree.Program): ESTree.Program {
  168. if (NodeGuards.isProgramNode(astTree) && !astTree.body.length) {
  169. this.logger.warn(LoggingMessage.EmptySourceCode);
  170. return astTree;
  171. }
  172. // first pass: AST-tree preparation
  173. this.logger.info(LoggingMessage.StagePreparingASTTree);
  174. astTree = this.transformersRunner.transform(
  175. astTree,
  176. JavaScriptObfuscator.preparingTransformersList
  177. );
  178. // second pass: AST-tree analyzing
  179. this.logger.info(LoggingMessage.StageAnalyzingASTTree);
  180. const stackTraceData: IStackTraceData[] = this.stackTraceAnalyzer.analyze(astTree);
  181. // initialize custom node groups and configure custom nodes
  182. this.customNodeGroupStorage
  183. .getStorage()
  184. .forEach((customNodeGroup: ICustomNodeGroup) => {
  185. customNodeGroup.initialize();
  186. this.obfuscationEventEmitter.once(
  187. customNodeGroup.getAppendEvent(),
  188. customNodeGroup.appendCustomNodes.bind(customNodeGroup)
  189. );
  190. });
  191. this.obfuscationEventEmitter.emit(ObfuscationEvent.BeforeObfuscation, astTree, stackTraceData);
  192. // third pass: dead code injection transformer
  193. if (this.options.deadCodeInjection) {
  194. this.logger.info(LoggingMessage.StageDeadCodeInjection);
  195. astTree = this.transformersRunner.transform(
  196. astTree,
  197. JavaScriptObfuscator.deadCodeInjectionTransformersList
  198. );
  199. }
  200. // fourth pass: control flow flattening transformers
  201. if (this.options.controlFlowFlattening) {
  202. this.logger.info(LoggingMessage.StageControlFlowFlattening);
  203. astTree = this.transformersRunner.transform(
  204. astTree,
  205. JavaScriptObfuscator.controlFlowTransformersList
  206. );
  207. }
  208. // fifth pass: converting and obfuscating transformers
  209. this.logger.info(LoggingMessage.StageObfuscation);
  210. astTree = this.transformersRunner.transform(astTree, [
  211. ...JavaScriptObfuscator.convertingTransformersList,
  212. ...JavaScriptObfuscator.obfuscatingTransformersList
  213. ]);
  214. this.obfuscationEventEmitter.emit(ObfuscationEvent.AfterObfuscation, astTree, stackTraceData);
  215. return astTree;
  216. }
  217. /**
  218. * @param {string} sourceCode
  219. * @param {Program} astTree
  220. * @returns {IGeneratorOutput}
  221. */
  222. private generateCode (sourceCode: string, astTree: ESTree.Program): IGeneratorOutput {
  223. const escodegenParams: escodegen.GenerateOptions = {
  224. ...JavaScriptObfuscator.escodegenParams
  225. };
  226. if (this.options.sourceMap) {
  227. escodegenParams.sourceMap = 'sourceMap';
  228. escodegenParams.sourceContent = sourceCode;
  229. }
  230. if (this.options.mangle) {
  231. astTree = esmangle.mangle(astTree);
  232. }
  233. const generatorOutput: IGeneratorOutput = escodegen.generate(astTree, {
  234. ...escodegenParams,
  235. format: {
  236. compact: this.options.compact
  237. }
  238. });
  239. generatorOutput.map = generatorOutput.map ? generatorOutput.map.toString() : '';
  240. return generatorOutput;
  241. }
  242. /**
  243. * @param {IGeneratorOutput} generatorOutput
  244. * @returns {IObfuscationResult}
  245. */
  246. private getObfuscationResult (generatorOutput: IGeneratorOutput): IObfuscationResult {
  247. return this.sourceMapCorrector.correct(
  248. generatorOutput.code,
  249. generatorOutput.map
  250. );
  251. }
  252. }