diff --git a/arkguard/.gitignore b/arkguard/.gitignore index c18ed016a7f867069687a82a35d5d592f56c6d30..b1fb9e0f465005d9380da0423d7af534e8eb93ae 100644 --- a/arkguard/.gitignore +++ b/arkguard/.gitignore @@ -1,2 +1,4 @@ node_modules/ -lib/ \ No newline at end of file +lib/ +.vscode/ +.idea/ \ No newline at end of file diff --git a/arkguard/README.md b/arkguard/README.md index ebf73869dbd9e06c60fc0abfca7ab9663ea752c9..b3f94ad99cd42456868b58bdfaec305ac7a510dc 100644 --- a/arkguard/README.md +++ b/arkguard/README.md @@ -23,7 +23,7 @@ When you create a new project, the following config will be generated in `build- "obfuscation": { "ruleOptions": { "enable": true, - "rules": ["obfusation-rules.txt"], + "rules": ["obfuscation-rules.txt"], } } } @@ -36,7 +36,7 @@ When you create a new library, additional property `consumerRules` will be added "obfuscation": { "ruleOptions": { "enable": true, - "rules": ["obfusation-rules.txt"], + "rules": ["obfuscation-rules.txt"], } "consumerRules": ["consumer-rules.txt"] } @@ -61,7 +61,7 @@ HAP, `obfuscation.txt` will not be generated. ## Write rules -The files `obfusation-rules.txt` and `consumer-rules.txt` are created by DevEco Studio automatically, but they do not +The files `obfuscation-rules.txt` and `consumer-rules.txt` are created by DevEco Studio automatically, but they do not contain any rule by default. You can write rules in these files or include rules from other files, as the following example shows. ``` @@ -70,7 +70,7 @@ example shows. "obfuscation": { "ruleOptions": { "enable": true, - "rules": ["obfusation-rules.txt", "myrules.txt"], + "rules": ["obfuscation-rules.txt", "myrules.txt"], } "consumerRules": ["consumer-rules.txt", "my-consumer-rules.txt"] } @@ -82,7 +82,7 @@ In rule files, you can write [obfuscation options](#obfuscation-options) and [ke ### Obfuscation options -`-disable-obfusation` +`-disable-obfuscation` Specifies to disable all obfuscations. If you use this option, the resulting HAP or HAR will not be obfuscated. By default, Arkguard only obfuscates the parameter names and local variable names by assigning random short names to them. @@ -91,7 +91,16 @@ Arkguard only obfuscates the parameter names and local variable names by assigni Specifies to obfuscate the property names. If you use this option, all property names will be obfuscated except the following: -* the property names of `import/export` classes or objects. +* the property names of `import/export` classes or objects. Note: Only for directly exported classes or objects, their +property names will be kept. For example, the property name `data` in + ``` + export class MyClass { + data: string; + } + ``` + will not be obfuscated. +In 'directly export' cases such as `export MyClass` and `let a = MyClass; export a;`, if you do not want to obfuscate +their property names, you need to use [keep options](#keep-options) to keep them. * the property names defined in UI components. For example, the property names `message` and `data` in ``` @Component struct MyExample { @@ -179,6 +188,15 @@ console.log(obj['t']); // t and 't' can be safely obfuscated, but we sugg obj.['v'] = 0; console.log(obj['v']); // 'v' can be safely obfuscated, but we suggest keeping v ``` +The property names defined in UI components should also be kept for now. In the future, we will keep them automatically. +``` +@Entry +@Component +struct Index { + @State messaage: string = 'Hello World'; // messaage should be kept + foo(){}; // foo should be kept +} +``` `-keep-global-name` [,modifiers,...] @@ -233,4 +251,59 @@ firstName lastName age ``` -If your are building HAR, comments will not be merged into the resulting `obfuscation.txt`. \ No newline at end of file +If your are building HAR, comments will not be merged into the resulting `obfuscation.txt`. + +### How Arkguard merges rules? +Typically there may be serveral rule files in your project. These rule files come from: +* `ruleOptions.rules` in main project (Here by main project we mean the project you are building) +* `consumerRules` in local dependency libraries +* `obfuscate.txt` in remote dependency HARs +When building your main project, all these rules will be merged by the following strategy (in pseudo code): +``` +let `listRules` be the list of all rule files that are mentioned above. +let finalRule = { + disableObfuscation: false, + enablePropertyObfuscation: false, + enableToplevelObfuscation: false, + compact: false, + removeLog: false, + keepPropertyName: [], + keepGlobalName: [], + keepDts: [] +} +for each file in `listRules`: + for each option in file: + switch(option) { + case -disable-obfuscation: + finalRule.disableObfuscation = true; + continue; + case -enable-property-obfuscation: + finalRule.enablePropertyObfuscation = true; + continue; + case -enable-toplevel-obfuscation: + finalRule.enableToplevelObfuscation = true; + continue; + case -compact: + finalRule.compact = true; + continue; + case -remove-log: + finalRule.removeLog = true; + continue; + case -keep-property-name: + finalRule.keepPropertyName.push(#{specified names}); + continue; + case -keep-global-name: + finalRule.keepGlobalName.push(#{specified names}); + continue; + case -keep-dts: + finalRule.keepDts.push(#{specified file paths}); + continue; + } + end-for +end-for +``` +The final obfuscation rules are in the object `finalRule`. + +If you are building HAR, the resulting `obfuscate.txt` are obtained by merging the rules from `consumerRules` in main +project and local dependency libraries, and `obfuscate.txt` in remote dependency HARs. The merging strategy is the same +as above. diff --git a/arkguard/build_package/arkguard-1.0.0.tgz b/arkguard/build_package/arkguard-1.0.0.tgz index 64b2e3d1e1e1519ace2dcf2ac25bdf5af216ab46..11b852ae29c3571952b2a6c109dd5cfe3753c0cd 100644 Binary files a/arkguard/build_package/arkguard-1.0.0.tgz and b/arkguard/build_package/arkguard-1.0.0.tgz differ diff --git a/arkguard/package.json b/arkguard/package.json index 5b3496dd120f0f2ccc6ddf0220a39f059bcf5aa7..3ed59164f53611d584bf217006549f301e60ac61 100644 --- a/arkguard/package.json +++ b/arkguard/package.json @@ -38,7 +38,8 @@ "dependencies": { "commander": "^9.3.0", "fs-extra": "^10.1.0", - "source-map":"0.7.4" + "json5": "^2.2.3", + "source-map": "0.7.4" }, "files": [ "bin", diff --git a/arkguard/scripts/grammarTestConfig.json b/arkguard/scripts/grammarTestConfig.json index ae6aa84637b7132fdcfc7424a846003eec9cba50..acd923c8e7d63fab98b9ac16b62f20e9ae2fae31 100644 --- a/arkguard/scripts/grammarTestConfig.json +++ b/arkguard/scripts/grammarTestConfig.json @@ -1,16 +1,19 @@ { "mCompact": false, - "mRemoveComments": true, - "mOutputDir": "../test/local/", - "mDisableHilog": true, - "mDisableConsole":true, + "mRemoveComments": false, + "mOutputDir": "../test/local", + "mDisableHilog": false, + "mDisableConsole":false, + "mSimplify": false, "mNameObfuscation": { "mEnable": true, "mNameGeneratorType": 1, + "mDictionaryList": [], + "mReservedNames": [], "mRenameProperties": true, - "mReservedNames": [] + "mReservedProperties": ["strictEqual"] }, - "mEnableSourceMap": true, + "mEnableSourceMap": false, "mEnableNameCache": false, "mTopLevel":true } \ No newline at end of file diff --git a/arkguard/src/ArkObfuscator.ts b/arkguard/src/ArkObfuscator.ts index cf20c9b9594267afef1b7b000cf62c118f7b4f5a..04f27aedb54db6d76299f38292046627bf4015fd 100644 --- a/arkguard/src/ArkObfuscator.ts +++ b/arkguard/src/ArkObfuscator.ts @@ -15,12 +15,8 @@ import { createPrinter, - createProgram, createSourceFile, createTextWriter, - forEachChild, - isIdentifier, - isTypePredicateNode, - ScriptTarget, + ScriptTarget, transform, createObfTextSingleLineWriter, } from 'typescript'; @@ -28,20 +24,13 @@ import { import type { CompilerOptions, EmitTextWriter, - JSDocReturnTag, - JSDocSignature, Node, Printer, PrinterOptions, - Program, - Signature, - SignatureDeclaration, SourceFile, SourceMapGenerator, TransformationResult, TransformerFactory, - TypeChecker, - TypeNode } from 'typescript'; import * as fs from 'fs'; @@ -60,14 +49,15 @@ import { readCache, writeCache } from './utils/NameCacheUtil'; import {ListUtil} from './utils/ListUtil'; -import {needReadApiInfo, getReservedProperties, readProjectProperties} from './common/ApiReader'; +import {needReadApiInfo, readProjectProperties} from './common/ApiReader'; import {ApiExtractor} from './common/ApiExtractor'; import es6Info from './configs/preset/es6_reserved_properties.json'; export const renameIdentifierModule = require('./transformers/rename/RenameIdentifierTransformer'); export const renamePropertyModule = require('./transformers/rename/RenamePropertiesTransformer'); -export { getMapFromJson, readProjectProperties }; +export {getMapFromJson, readProjectProperties}; + export class ArkObfuscator { // A text writer of Printer private mTextWriter: EmitTextWriter; @@ -86,8 +76,6 @@ export class ArkObfuscator { private mTransformers: TransformerFactory[]; - private mNeedCollectNarrowFunction: boolean; - public constructor(sourceFiles?: string[], configPath?: string) { this.mSourceFiles = sourceFiles; this.mConfigPath = configPath; @@ -110,8 +98,6 @@ export class ArkObfuscator { this.mCustomProfiles = config as IOptions; - - if (this.mCustomProfiles.mCompact) { this.mTextWriter = createObfTextSingleLineWriter(); } else { @@ -122,20 +108,16 @@ export class ArkObfuscator { this.mCompilerOptions.sourceMap = true; } - // load transformers this.mTransformers = TransformerManager.getInstance().loadTransformers(this.mCustomProfiles); - // check need collect narrow function names - this.mNeedCollectNarrowFunction = this.checkNeedCollectNarrowFunction(); - if (needReadApiInfo(this.mCustomProfiles)) { this.mCustomProfiles.mNameObfuscation.mReservedProperties = ListUtil.uniqueMergeList( this.mCustomProfiles.mNameObfuscation.mReservedProperties, this.mCustomProfiles.mNameObfuscation.mReservedNames, es6Info); } - + return true; } @@ -146,13 +128,13 @@ export class ArkObfuscator { if (!path.isAbsolute(this.mCustomProfiles.mOutputDir)) { this.mCustomProfiles.mOutputDir = path.join(path.dirname(this.mConfigPath), this.mCustomProfiles.mOutputDir); } + if (this.mCustomProfiles.mOutputDir && !fs.existsSync(this.mCustomProfiles.mOutputDir)) { fs.mkdirSync(this.mCustomProfiles.mOutputDir); } - readProjectProperties(this.mSourceFiles, this.mCustomProfiles); - this.readPropertyCache(this.mCustomProfiles.mOutputDir); + // support directory and file obfuscate for (const sourcePath of this.mSourceFiles) { if (!fs.existsSync(sourcePath)) { @@ -168,6 +150,7 @@ export class ArkObfuscator { const dirPrefix: string = FileUtils.getPrefix(sourcePath); await this.obfuscateDir(sourcePath, dirPrefix); } + this.producePropertyCache(this.mCustomProfiles.mOutputDir); } @@ -198,45 +181,6 @@ export class ArkObfuscator { } } - private checkNeedCollectNarrowFunction(): boolean { - return this.mCustomProfiles.mControlFlowFlattening && - this.mCustomProfiles.mControlFlowFlattening.mEnable && - this.mCustomProfiles.mInstructionObfuscation && - this.mCustomProfiles.mInstructionObfuscation.mEnable; - } - - private collectNarrowFunctions(file: string): void { - if (!this.mNeedCollectNarrowFunction) { - return; - } - - let results: Set = new Set(); - - let program: Program = createProgram([file], this.mCompilerOptions); - let checker: TypeChecker = program.getTypeChecker(); - let visit = (node: Node): void => { - if (!node) { - return; - } - - if (isIdentifier(node)) { - let type: Signature = checker.getTypeAtLocation(node).getCallSignatures()[0]; - let declaration: SignatureDeclaration | JSDocSignature = type?.declaration; - let retType: TypeNode | JSDocReturnTag = declaration?.type; - if (retType && isTypePredicateNode(retType)) { - results.add(node.text); - } - } - - forEachChild(node, visit); - }; - - let ast: SourceFile = program.getSourceFile(file); - visit(ast); - - this.mCustomProfiles.mNarrowFunctionNames = [...results]; - } - private readNameCache(sourceFile: string, outputDir: string): void { if (!this.mCustomProfiles.mNameObfuscation.mEnable || !this.mCustomProfiles.mEnableNameCache) { return; @@ -249,15 +193,17 @@ export class ArkObfuscator { } private readPropertyCache(outputDir: string): void { + if (!this.mCustomProfiles.mNameObfuscation.mRenameProperties || !this.mCustomProfiles.mEnableNameCache) { + return; + } + const propertyCachePath: string = path.join(outputDir, PROPERTY_CACHE_FILE); const propertyCache: Object = readCache(propertyCachePath); if (!propertyCache) { return; } - if (this.mCustomProfiles.mNameObfuscation.mRenameProperties) { - renamePropertyModule.historyMangledTable = getMapFromJson(propertyCache); - } + renamePropertyModule.historyMangledTable = getMapFromJson(propertyCache); } private produceNameCache(namecache: any, sourceFile: string, outputDir: string): void { @@ -266,19 +212,21 @@ export class ArkObfuscator { } private producePropertyCache(outputDir: string): void { - if (this.mCustomProfiles.mNameObfuscation.mRenameProperties) { + if (this.mCustomProfiles.mNameObfuscation.mRenameProperties && this.mCustomProfiles.mEnableNameCache) { const propertyCachePath: string = path.join(outputDir, PROPERTY_CACHE_FILE); writeCache(renamePropertyModule.globalMangledTable, propertyCachePath); } } - async mergeSourcrMap(originMap: sourceMap.RawSourceMap, newMap: sourceMap.RawSourceMap): Promise { + async mergeSourceMap(originMap: sourceMap.RawSourceMap, newMap: sourceMap.RawSourceMap): Promise { if (!originMap) { return newMap; } + if (!newMap) { return originMap; } + const originConsumer: sourceMap.SourceMapConsumer = await new sourceMap.SourceMapConsumer(originMap); const newConsumer: sourceMap.SourceMapConsumer = await new sourceMap.SourceMapConsumer(newMap); const newMappingList: sourceMap.MappingItem[] = []; @@ -286,14 +234,21 @@ export class ArkObfuscator { if (mapping.originalLine == null) { return; } - const originalPos = originConsumer.originalPositionFor({line: mapping.originalLine, column: mapping.originalColumn}); + + const originalPos = originConsumer.originalPositionFor({ + line: mapping.originalLine, + column: mapping.originalColumn + }); + if (originalPos.source == null) { return; } + mapping.originalLine = originalPos.line; mapping.originalColumn = originalPos.column; newMappingList.push(mapping); }); + const updatedGenerator: sourceMap.SourceMapGenerator = sourceMap.SourceMapGenerator.fromSourceMap(newConsumer); updatedGenerator['_file'] = originMap.file; updatedGenerator['_mappings']['_array'] = newMappingList; @@ -309,32 +264,34 @@ export class ArkObfuscator { if (this.mCustomProfiles.mRemoveComments) { printerOptions.removeComments = true; } + return createPrinter(printerOptions); } + private isObfsIgnoreFile(fileName: string): boolean { + let suffix: string = FileUtils.getFileExtension(fileName); + + return (suffix !== 'js' && suffix !== 'ts') || fileName.endsWith('.d.ts'); + } + /** - * Obfuscate single source file + * Obfuscate single source file with path provided * - * @param sourceFile single source file path + * @param sourceFilePath single source file path * @param outputDir */ public async obfuscateFile(sourceFilePath: string, outputDir: string): Promise { const fileName: string = FileUtils.getFileName(sourceFilePath); - let suffix: string = FileUtils.getFileExtension(sourceFilePath); - - if ((suffix !== 'js' && suffix !== 'ts') || fileName.endsWith('.d.ts')) { + if (this.isObfsIgnoreFile(fileName)) { fs.copyFileSync(sourceFilePath, path.join(outputDir, fileName)); return; } - // Advanced confusion requires calling this function - this.collectNarrowFunctions(sourceFilePath); - let content: string = FileUtils.readFile(sourceFilePath); this.readNameCache(sourceFilePath, outputDir); - const mixedInfo: {content: string, sourceMap, nameCache: Map} = await this.obfuscate(content, sourceFilePath); + const mixedInfo: {content, sourceMap, nameCache} = await this.obfuscate(content, sourceFilePath); - if (outputDir) { + if (outputDir && mixedInfo) { fs.writeFileSync(path.join(outputDir, FileUtils.getFileName(sourceFilePath)), mixedInfo.content); if (this.mCustomProfiles.mEnableSourceMap && mixedInfo.sourceMap) { fs.writeFileSync(path.join(outputDir, FileUtils.getFileName(sourceFilePath) + '.map'), JSON.stringify(mixedInfo.sourceMap, null, 2)); @@ -343,25 +300,31 @@ export class ArkObfuscator { this.produceNameCache(mixedInfo.nameCache, sourceFilePath, outputDir); } } - } /** * Obfuscate ast of a file. - * @param ast ast of a source file + * @param content ast or source code of a source file + * @param sourceFilePath + * @param previousStageSourceMap + * @param historyNameCache */ public async obfuscate(content: SourceFile | string, sourceFilePath: string, previousStageSourceMap?: any, historyNameCache?: Map): Promise { let ast: SourceFile; + if (this.isObfsIgnoreFile(sourceFilePath)) { + // need add return value + return; + } + if (typeof content === 'string') { ast = createSourceFile(sourceFilePath, content, ScriptTarget.ES2015, true); - } - else { + } else { ast = content; } if (ast.statements.length === 0) { - return ast; + return; } if (historyNameCache && this.mCustomProfiles.mNameObfuscation) { @@ -379,24 +342,28 @@ export class ArkObfuscator { this.createObfsPrinter().writeFile(ast, this.mTextWriter, sourceMapGenerator); - const result = { content: this.mTextWriter.getText() }; + const result = {content: this.mTextWriter.getText()}; - if (this.mCustomProfiles.mEnableSourceMap) { + if (this.mCustomProfiles.mEnableSourceMap && sourceMapGenerator) { let sourceMapJson = sourceMapGenerator.toJSON(); sourceMapJson['sourceRoot'] = ''; sourceMapJson.file = path.basename(sourceFilePath); if (previousStageSourceMap) { - sourceMapJson = await this.mergeSourcrMap(previousStageSourceMap, sourceMapJson as sourceMap.RawSourceMap); + sourceMapJson = await this.mergeSourceMap(previousStageSourceMap, sourceMapJson as sourceMap.RawSourceMap); } result['sourceMap'] = sourceMapJson; } + if (this.mCustomProfiles.mEnableNameCache) { result['nameCache'] = Object.fromEntries(renameIdentifierModule.nameCache); } + // clear cache of text writer this.mTextWriter.clear(); - renameIdentifierModule.nameCache.clear(); + if (renameIdentifierModule.nameCache) { + renameIdentifierModule.nameCache.clear(); + } return result; } } -export {ApiExtractor}; \ No newline at end of file +export {ApiExtractor}; diff --git a/arkguard/src/common/ApiExtractor.ts b/arkguard/src/common/ApiExtractor.ts index 9d15871324d52d613011a1b734eeb2e64706bdc5..4ccb8eb1e3a3b387b3f71a7d236f723e460ad679 100644 --- a/arkguard/src/common/ApiExtractor.ts +++ b/arkguard/src/common/ApiExtractor.ts @@ -13,39 +13,36 @@ * limitations under the License. */ +import {isStructDeclaration, ModifiersArray, SourceFile} from 'typescript'; import { createSourceFile, forEachChild, isBinaryExpression, - isClassDeclaration, + isClassDeclaration, isClassExpression, isEnumDeclaration, isEnumMember, isExportAssignment, isExportDeclaration, isExportSpecifier, isFunctionDeclaration, + isIdentifier, isInterfaceDeclaration, isMethodDeclaration, isMethodSignature, - isModuleDeclaration, + isObjectLiteralExpression, isPropertyDeclaration, isPropertySignature, isTypeAliasDeclaration, isVariableDeclaration, isVariableStatement, - Node, ScriptKind, ScriptTarget, SyntaxKind } from 'typescript'; -import type { - ModifiersArray, - SourceFile -} from 'typescript'; - import fs from 'fs'; import path from 'path'; +import {getClassProperties, getEnumProperties, getObjectProperties} from '../utils/OhsUtil'; export namespace ApiExtractor { interface KeywordInfo { @@ -56,19 +53,21 @@ export namespace ApiExtractor { export enum ApiType { API = 1, COMPONENT = 2, - PROJECTDEPENDENCY = 3, + PROJECT_DEPENDS = 3, PROJECT = 4 } - let mExportNameList: string[] = []; - let mCurrentExportNameList: string[] = []; - export let mPropertyList: string[] = []; - export let mNameList: string[] = []; + let mCurrentExportNameSet: Set = new Set(); + export let mPropertySet: Set = new Set(); /** * filter classes or interfaces with export, default, etc */ const getKeyword = function (modifiers: ModifiersArray): KeywordInfo { + if (modifiers === undefined) { + return {hasExport: false, hasDeclare: false}; + } + let hasExport: boolean = false; let hasDeclare: boolean = false; @@ -91,48 +90,44 @@ export namespace ApiExtractor { */ const visitExport = function (astNode): void { if (isExportAssignment(astNode)) { - if (!mCurrentExportNameList.includes(astNode.expression.getText())) { - mCurrentExportNameList.push(astNode.expression.getText()); + if (!mCurrentExportNameSet.has(astNode.expression.getText())) { + mCurrentExportNameSet.add(astNode.expression.getText()); + mPropertySet.add(astNode.expression.getText()); } return; } - if (astNode.modifiers === undefined) { - return; - } - let {hasExport, hasDeclare} = getKeyword(astNode.modifiers); if (!hasExport) { return; } if (astNode.name) { - if (!mCurrentExportNameList.includes(astNode.name.getText())) { - mCurrentExportNameList.push(astNode.name.getText()); + if (!mCurrentExportNameSet.has(astNode.name.getText())) { + mCurrentExportNameSet.add(astNode.name.getText()); + mPropertySet.add(astNode.name.getText()); } return; } if (hasDeclare && astNode.declarationList && - !mCurrentExportNameList.includes(astNode.declarationList.declarations[0].name.getText())) { - mCurrentExportNameList.push(astNode.declarationList.declarations[0].name.getText()); + !mCurrentExportNameSet.has(astNode.declarationList.declarations[0].name.getText())) { + mCurrentExportNameSet.add(astNode.declarationList.declarations[0].name.getText()); + mPropertySet.add(astNode.declarationList.declarations[0].name.getText()); } }; const checkPropertyNeedVisit = function (astNode): boolean { - if (astNode.name && !mCurrentExportNameList.includes(astNode.name.getText())) { + if (astNode.name && !mCurrentExportNameSet.has(astNode.name.getText())) { return false; } if (astNode.name === undefined) { - if (astNode.modifiers === undefined) { - return false; - } let {hasDeclare} = getKeyword(astNode.modifiers); if (hasDeclare && astNode.declarationList && - !mCurrentExportNameList.includes(astNode.declarationList.declarations[0].name.getText())) { + !mCurrentExportNameSet.has(astNode.declarationList.declarations[0].name.getText())) { return false; } } @@ -140,6 +135,10 @@ export namespace ApiExtractor { return true; }; + /** + * used only in oh sdk api extract or api of xxx.d.ts declaration file + * @param astNode + */ const visitChildNode = function (astNode): void { if (isClassDeclaration(astNode) || isInterfaceDeclaration(astNode) || @@ -153,14 +152,8 @@ export namespace ApiExtractor { isEnumMember(astNode) || isExportSpecifier(astNode) || isVariableDeclaration(astNode)) { - if (astNode.name !== undefined ) { - const name = astNode.name.getText(); - if (!mPropertyList.includes(name)) { - mPropertyList.push(name); - } - if (!mNameList.includes(name)) { - mNameList.push(name); - } + if (astNode.name !== undefined && !mPropertySet.has(astNode.name.getText())) { + mPropertySet.add(astNode.name.getText()); } } @@ -171,6 +164,7 @@ export namespace ApiExtractor { /** * visit ast of a file and collect api list + * used only in oh sdk api extract * @param astNode node of ast */ const visitPropertyAndName = function (astNode): void { @@ -181,69 +175,143 @@ export namespace ApiExtractor { visitChildNode(astNode); }; - const visitProjectNode = function (astNode): void { - if (astNode.modifiers) { - let {hasExport} = getKeyword(astNode.modifiers); - if (!hasExport) { - return; - } + /** + * extract project export name + * - export {xxx, xxx}; + * - export {xxx as xx, xxx as xx}; + * - export default function/class/...{}; + * - export class xxx{} + * - ... + * @param astNode + */ + const visitProjectExport = function (astNode): void { + if (isExportAssignment(astNode)) { + // let xxx; export default xxx = a; + if (isBinaryExpression(astNode.expression)) { + if (isObjectLiteralExpression(astNode.expression.right)) { + getObjectProperties(astNode.expression.right, mPropertySet); + return; + } - if (astNode.name !== undefined) { - if (!mPropertyList.includes(astNode.name.getText())) { - mPropertyList.push(astNode.name.getText()); + if (isClassExpression(astNode.expression.right)) { + getClassProperties(astNode.expression.right, mPropertySet); } - if (isModuleDeclaration(astNode)) { - astNode.forEachChild((childNode) => { - visitProjectNode(childNode); - }); + return; + } + + // export = xxx; The xxx here can't be obfuscated + // export default yyy; The yyy here can be obfuscated + if (isIdentifier(astNode.expression)) { + if (!mCurrentExportNameSet.has(astNode.expression.getText())) { + mCurrentExportNameSet.add(astNode.expression.getText()); + mPropertySet.add(astNode.expression.getText()); } return; } - if (isVariableStatement(astNode)) { - astNode.declarationList.forEachChild((child) => { - if (isVariableDeclaration(child) && !mPropertyList.includes(child.name.getText())) { - mPropertyList.push(child.name.getText()); - } - }); + if (isObjectLiteralExpression(astNode.expression)) { + getObjectProperties(astNode.expression, mPropertySet); } return; } - if (isExportAssignment(astNode)) { - if (isBinaryExpression(astNode.expression)) { - if (!mPropertyList.includes(astNode.expression.left.getText())) { - mPropertyList.push(astNode.expression.left.getText()); + if (isExportDeclaration(astNode)) { + if (astNode.exportClause) { + if (astNode.exportClause.kind === SyntaxKind.NamedExports) { + astNode.exportClause.forEachChild((child) => { + if (!isExportSpecifier(child)) { + return; + } + + if (child.propertyName) { + mCurrentExportNameSet.add(child.propertyName.getText()); + } + + let exportName = child.name.getText(); + mPropertySet.add(exportName); + mCurrentExportNameSet.add(exportName); + }); + } + + if (astNode.exportClause.kind === SyntaxKind.NamespaceExport) { + mPropertySet.add(astNode.exportClause.name.getText()); + return; } } return; } - if (isExportDeclaration(astNode)) { - if (astNode.exportClause && astNode.exportClause.kind === SyntaxKind.NamedExports) { - astNode.exportClause.forEachChild((child) => { - if (!isExportSpecifier(child)) { - return; - } - - if (!mPropertyList.includes(child.name.getText())) { - mPropertyList.push(child.name.getText()); - } - }); + let {hasExport} = getKeyword(astNode.modifiers); + if (!hasExport) { + forEachChild(astNode, visitProjectExport); + return; + } + + if (astNode.name) { + if (!mCurrentExportNameSet.has(astNode.name.getText())) { + mCurrentExportNameSet.add(astNode.name.getText()); + mPropertySet.add(astNode.name.getText()); } + forEachChild(astNode, visitProjectExport); return; } - astNode.forEachChild((childNode) => { - visitProjectNode(childNode); - }); + if (isClassDeclaration(astNode)) { + getClassProperties(astNode, mPropertySet); + return; + } + + if (isVariableStatement(astNode)) { + astNode.declarationList.forEachChild((child) => { + if (isVariableDeclaration(child) && !mCurrentExportNameSet.has(child.name.getText())) { + mCurrentExportNameSet.add(child.name.getText()); + mPropertySet.add(child.name.getText()); + } + }); + + return; + } + + forEachChild(astNode, visitProjectExport); }; - const visitProjectProperty = function (astNode): void { - visitProjectNode(astNode); + /** + * extract the class, enum, and object properties of the export in the project before obfuscation + * class A{}; + * export = A; need to be considered + * export = namespace; + * This statement also needs to determine whether there is an export in the namespace, and namespaces are also allowed in the namespace + * @param astNode + */ + const visitProjectNode = function (astNode): void { + if ((isClassDeclaration(astNode) || isStructDeclaration(astNode)) && astNode.name && mCurrentExportNameSet.has(astNode.name.text)) { + getClassProperties(astNode, mPropertySet); + return; + } + + // collect export enum structure properties + if (isEnumDeclaration(astNode) && mCurrentExportNameSet.has(astNode.name.getText())) { + getEnumProperties(astNode, mPropertySet); + return; + } + + if (isVariableDeclaration(astNode) && mCurrentExportNameSet.has(astNode.name.getText())) { + if (astNode.initializer && isObjectLiteralExpression(astNode.initializer)) { + getObjectProperties(astNode.initializer, mPropertySet); + return; + } + + if (astNode.initializer && isClassExpression(astNode.initializer)) { + getClassProperties(astNode.initializer, mPropertySet); + } + + return; + } + + forEachChild(astNode, visitProjectNode); }; /** @@ -253,44 +321,39 @@ export namespace ApiExtractor { * @private */ const parseFile = function (fileName: string, apiType: ApiType): void { - const scriptKind: ScriptKind = fileName.endsWith('.ts') ? ScriptKind.TS : ScriptKind.JS; - const sourceFile: SourceFile = createSourceFile(fileName, fs.readFileSync(fileName).toString(), ScriptTarget.ES2015, true, scriptKind); + const sourceFile: SourceFile = createSourceFile(fileName, fs.readFileSync(fileName).toString(), ScriptTarget.ES2015, true); // get export name list switch (apiType) { - case ApiType.PROJECTDEPENDENCY: case ApiType.COMPONENT: forEachChild(sourceFile, visitChildNode); break; case ApiType.API: - mCurrentExportNameList.length = 0; + mCurrentExportNameSet.clear(); forEachChild(sourceFile, visitExport); - mCurrentExportNameList.forEach((value) => { - if (!mExportNameList.includes(value)) { - mExportNameList.push(value); - mNameList.push(value); - } - }); - forEachChild(sourceFile, visitPropertyAndName); - mCurrentExportNameList.length = 0; + mCurrentExportNameSet.clear(); break; + case ApiType.PROJECT_DEPENDS: case ApiType.PROJECT: - if (fileName.endsWith('.d.ts')) { + if (fileName.endsWith('.d.ts') || fileName.endsWith('.d.ets')) { forEachChild(sourceFile, visitChildNode); break; } - mCurrentExportNameList.length = 0; - forEachChild(sourceFile, visitProjectProperty); - mCurrentExportNameList.length = 0; + mCurrentExportNameSet.clear(); + forEachChild(sourceFile, visitProjectExport); + forEachChild(sourceFile, visitProjectNode); + mCurrentExportNameSet.clear(); break; default: break; } }; + const projectExtensions: string[] = ['.ets', '.ts', '.js']; + const projectDependencyExtensions: string[] = ['.d.ets', '.d.ts', '.ets', '.ts', '.js']; /** * traverse files of api directory * @param apiPath api directory path @@ -299,39 +362,25 @@ export namespace ApiExtractor { */ export const traverseApiFiles = function (apiPath: string, apiType: ApiType): void { let fileNames: string[] = []; - if (fs.lstatSync(apiPath).isDirectory()) { + if (fs.statSync(apiPath).isDirectory()) { fileNames = fs.readdirSync(apiPath); for (let fileName of fileNames) { let filePath: string = path.join(apiPath, fileName); - if (fs.lstatSync(filePath).isDirectory()) { - if (fileName === 'node_modules' || fileName === 'oh_modules') { - continue; - } - + if (fs.statSync(filePath).isDirectory()) { traverseApiFiles(filePath, apiType); continue; } - - if (fs.lstatSync(filePath).isSymbolicLink()) { - filePath = fs.readlinkSync(filePath); - if (fs.lstatSync(filePath).isDirectory()) { - traverseApiFiles(filePath, apiType); - continue; - } - } - - if ((apiType !== ApiType.PROJECT) && !filePath.endsWith('.d.ts')) { + const suffix: string = path.extname(filePath); + if ((apiType !== ApiType.PROJECT) && !projectDependencyExtensions.includes(suffix)) { continue; } - if (apiType === ApiType.PROJECT && !filePath.endsWith('.ts') && !filePath.endsWith('.js')) { + if (apiType === ApiType.PROJECT && !projectExtensions.includes(suffix)) { continue; } - parseFile(filePath, apiType); } - } - else { + } else { parseFile(apiPath, apiType); } }; @@ -344,8 +393,7 @@ export namespace ApiExtractor { * @param outputDir: sdk api output directory */ export function parseOhSdk(sdkPath: string, version: string, isEts: boolean, outputDir: string): void { - mExportNameList.length = 0; - mPropertyList.length = 0; + mPropertySet.clear(); // visit api directory const apiPath: string = path.join(sdkPath, (isEts ? 'ets' : 'js'), version, 'api'); @@ -358,13 +406,14 @@ export namespace ApiExtractor { } // visit the UI conversion API - const uiConversionPath: string = path.join(sdkPath, (isEts ? 'ets' : 'js'), version, 'build-tools', 'ets-loader', 'lib', 'pre_define.js'); + const uiConversionPath: string = path.join(sdkPath, (isEts ? 'ets' : 'js'), version, + 'build-tools', 'ets-loader', 'lib', 'pre_define.js'); extractStringsFromFile(uiConversionPath); - writeToFile(mExportNameList, path.join(outputDir, 'nameReserved.json')); - writeToFile(mPropertyList, path.join(outputDir, 'propertiesReserved.json')); - mExportNameList.length = 0; - mPropertyList.length = 0; + const reservedProperties: string[] = [...mPropertySet.values()]; + mPropertySet.clear(); + + writeToFile(reservedProperties, path.join(outputDir, 'propertiesReserved.json')); } export function extractStringsFromFile(filePath: string): void { @@ -377,8 +426,7 @@ export namespace ApiExtractor { collections = matches.map(match => match.slice(1, -1)); } - mPropertyList = mPropertyList.concat(collections); - mNameList = mNameList.concat(collections); + collections.forEach(name => mPropertySet.add(name)); } /** @@ -386,18 +434,18 @@ export namespace ApiExtractor { * @return reserved api names */ export function parseCommonProject(projectPath): string[] { - mPropertyList.length = 0; + mPropertySet.clear(); if (fs.lstatSync(projectPath).isFile()) { - if (projectPath.endsWith('.ts') || projectPath.endsWith('.js')) { + if (projectPath.endsWith('.ets') || projectPath.endsWith('.ts') || projectPath.endsWith('.js')) { parseFile(projectPath, ApiType.PROJECT); } } else { traverseApiFiles(projectPath, ApiType.PROJECT); } - const reservedProperties: string[] = [...mPropertyList]; - mPropertyList.length = 0; + const reservedProperties: string[] = [...mPropertySet]; + mPropertySet.clear(); return reservedProperties; } @@ -407,18 +455,21 @@ export namespace ApiExtractor { * @param libPath */ export function parseThirdPartyLibs(libPath): string[] { - mPropertyList.length = 0; + mPropertySet.clear(); if (fs.lstatSync(libPath).isFile()) { - if (libPath.endsWith('.ts') || libPath.endsWith('.js')) { - parseFile(libPath, ApiType.PROJECTDEPENDENCY); + if (libPath.endsWith('.ets') || libPath.endsWith('.ts') || libPath.endsWith('.js')) { + parseFile(libPath, ApiType.PROJECT_DEPENDS); } } else { - traverseApiFiles(libPath, ApiType.PROJECTDEPENDENCY); + const filesAndfolders = fs.readdirSync(libPath); + for (let subPath of filesAndfolders) { + traverseApiFiles(path.join(libPath, subPath), ApiType.PROJECT_DEPENDS); + } } - const reservedProperties: string[] = [...mPropertyList]; - mPropertyList.length = 0; + const reservedProperties: string[] = [...mPropertySet]; + mPropertySet.clear(); return reservedProperties; } diff --git a/arkguard/src/common/ApiReader.ts b/arkguard/src/common/ApiReader.ts index 3c8542d14f6ff46a2d50dbd7e615b112649ea01f..c1cb4a78459b7f3fa4113bcd90b6d2d09fd2e191 100644 --- a/arkguard/src/common/ApiReader.ts +++ b/arkguard/src/common/ApiReader.ts @@ -96,18 +96,19 @@ export function needReadApiInfo(customProfiles: IOptions): boolean { * @param projectPaths can be dir or file * @param customProfiles */ -export function readProjectProperties(projectPaths: string[], customProfiles: IOptions): void { - if (!needReadApiInfo(customProfiles)) { - return; +export function readProjectProperties(projectPaths: string[], customProfiles: IOptions, isOHProject?: boolean): string[] { + if (!needReadApiInfo(customProfiles) && !isOHProject) { + return []; } for (const projectPath of projectPaths) { if (!fs.existsSync(projectPath)) { console.error(`File ${FileUtils.getFileName(projectPath)} is not found.`); - return; + return []; } - const projProperties: string[] = ApiExtractor.parseCommonProject(projectPath); + const sourcPath = isOHProject? path.join(projectPath, "src", "main") : projectPath; + const projProperties: string[] = ApiExtractor.parseCommonProject(sourcPath); const sdkProperties: string[] = readThirdPartyLibProperties(projectPath); // read project code export names @@ -120,6 +121,7 @@ export function readProjectProperties(projectPaths: string[], customProfiles: IO customProfiles.mNameObfuscation.mReservedProperties); } } + return customProfiles.mNameObfuscation.mReservedProperties; } function readThirdPartyLibProperties(projectPath: string): string[] { @@ -143,8 +145,7 @@ function readThirdPartyLibProperties(projectPath: string): string[] { let filePath: string = ''; if (hasNodeModules) { filePath = path.join(projectPath, 'node_modules'); - } - else { + } else { filePath = path.join(projectPath, 'oh_modules'); } diff --git a/arkguard/src/configs/IBogusControlFlowOption.ts b/arkguard/src/configs/IBogusControlFlowOption.ts deleted file mode 100644 index fcc40f24a27178b8248c78e2ec2ed7a89680008b..0000000000000000000000000000000000000000 --- a/arkguard/src/configs/IBogusControlFlowOption.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Generation type of the inserted block statement - */ -export enum BogusBlockType { - /** - * Use other available block and rename variables in block - */ - OTHER_BLOCK_RENAME = 1, - - /** - * Use a deformation of current block, replaces with some basic operations - */ - CURRENT_BLOCK_DEFORM = 2, -} - -export interface IBogusControlFlowOption { - /** - * Whether to enable bogus control flow obfuscation - */ - readonly mEnable: boolean; - - /** - * Probability of inserting bogus control flow into the target node - */ - readonly mThreshold: number; - - /** - * skip obfuscation in loop for performance - */ - readonly mSkipLoop?: boolean; - - /** - * Whether to use opaque predicates - */ - readonly mUseOpaquePredicate: boolean; - - /** - * Generation type of the inserted bogus block. - */ - readonly mInsertBlockType: BogusBlockType; -} diff --git a/arkguard/src/configs/IControlFlowFatteningOption.ts b/arkguard/src/configs/IControlFlowFatteningOption.ts deleted file mode 100644 index d4ead7bc3aef6107fb0e3a1ad0c9c92950d48b58..0000000000000000000000000000000000000000 --- a/arkguard/src/configs/IControlFlowFatteningOption.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export interface IControlFlowFatteningOption { - /** - * Whether to enable control flow obfuscation - */ - readonly mEnable: boolean; - - /** - * Probability of control flow obfuscation - */ - readonly mThreshold: number; - - /** - * skip obfuscation in loop for performance - */ - readonly mSkipLoop?: boolean; - - /** - * advance switch - */ - readonly mAdvance?: boolean; - - /** - * Whether to flatten if statement - */ - readonly mIfFlattening?: boolean; - - /** - * Whether to flatten switch statement - */ - readonly mSwitchFlatting?: boolean; - - /** - * Whether to convert case constants to expression - */ - readonly mCaseToExpression?: boolean; -} diff --git a/arkguard/src/configs/IDataObfuscationOption.ts b/arkguard/src/configs/IDataObfuscationOption.ts deleted file mode 100644 index 74c1de9a699f582eca614e151cd3a3cecc9bceb9..0000000000000000000000000000000000000000 --- a/arkguard/src/configs/IDataObfuscationOption.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type {EncryptType} from '../transformers/data/StringUnit'; - -export interface IBooleanOption { - readonly mEnable: boolean; - - readonly mThreshold: number; - - /** - * skip obfuscation in loop for performance - */ - readonly mSkipLoop: boolean; -} - -export interface INumberOption { - readonly mEnable: boolean; - - readonly mThreshold: number; - - /** - * skip obfuscation in loop for performance - */ - readonly mSkipLoop: boolean; -} - -export interface IStringOption { - - readonly mEnable: boolean; - - readonly mThreshold: number; - - /** - * skip obfuscation in loop for performance - */ - readonly mSkipLoop: boolean; - - readonly mSkipProperty: boolean; - - readonly mSplitString: boolean; - - readonly mStringArray: boolean; - - readonly mStringArrayThreshold: number; - - readonly mEncryptType: EncryptType; - - readonly mStringArrayShuffle: boolean; - - readonly mStringArrayCallsTransform: boolean; - - readonly mStringArrayCallsThreshold: number; - - mReservedStrings: string[]; - - readonly mObfuscationString: string[]; -} - -export interface IDataObfuscationOption { - - readonly mEnable: boolean; - - readonly mNumberOption: INumberOption; - - readonly mStringOption: IStringOption; - - readonly mBooleanOption: IBooleanOption; -} diff --git a/arkguard/src/configs/IOptions.ts b/arkguard/src/configs/IOptions.ts index 4319bd3c25c9dbdef15e8fbe4270a3a862b2c163..4056ba8d944df52323a6e12af19bb80557ff2eaf 100644 --- a/arkguard/src/configs/IOptions.ts +++ b/arkguard/src/configs/IOptions.ts @@ -13,12 +13,7 @@ * limitations under the License. */ -import type {IControlFlowFatteningOption} from './IControlFlowFatteningOption'; -import type {IDataObfuscationOption} from './IDataObfuscationOption'; -import type {IBogusControlFlowOption} from './IBogusControlFlowOption'; import type {INameObfuscationOption} from './INameObfuscationOption'; -import type {IInstructionObfuscationOption} from './IInstructionObfuscationOption'; -import type {IHideOhApiOption} from './IHideOhApiOption'; export interface IOptions { // Whether to generate compact code @@ -36,26 +31,9 @@ export interface IOptions { // Whether to do code simplification, includes variable declarations merging, expression merging... readonly mSimplify?: boolean; - // whether to hide openHarmony api - readonly mHideOhApi?: IHideOhApiOption; - // Whether to do Name Obfuscation readonly mNameObfuscation?: INameObfuscationOption; - // Whether to insert bogus control flow. - readonly mBogusControlFlow?: IBogusControlFlowOption; - - // Whether to do control flow flattening - readonly mControlFlowFlattening?: IControlFlowFatteningOption; - - // Whether to do data obfuscation - readonly mDataObfuscation?: IDataObfuscationOption; - - // Whether to do Instruction obfuscation, includes obfuscating binary expression, logical expression, call expression. - readonly mInstructionObfuscation?: IInstructionObfuscationOption; - - mNarrowFunctionNames?: Array; - mOutputDir?: string; readonly mOhSdkPath?: string; diff --git a/arkguard/src/generator/ReservedNameGenerator.ts b/arkguard/src/generator/ReservedNameGenerator.ts index 99df5970e2cfb7d91c60c76c38c64d1d8fd80b3b..35afd87fef4f62a46b8c6b05fcc6e0534022dd30 100644 --- a/arkguard/src/generator/ReservedNameGenerator.ts +++ b/arkguard/src/generator/ReservedNameGenerator.ts @@ -26,7 +26,7 @@ const gReservedIdentifier = [ 'interface', 'export', 'readonly', 'private', 'public', 'extends', 'implements', 'constructor', 'this', 'static', 'protected', 'switch', 'case', 'default', 'typeof', 'instanceof', 'in', 'of', 'import', 'require', - 'module', 'from', 'abstract', 'async', 'namespace', 'arguments' + 'module', 'from', 'abstract', 'async', 'namespace', 'arguments', '__global' ]; const gTransformDict = { diff --git a/arkguard/src/transformers/bogus/BogusControlTransformer.ts b/arkguard/src/transformers/bogus/BogusControlTransformer.ts deleted file mode 100644 index c924ecf591bc4b5af0e60fd687f1ddf4c6f2c494..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/bogus/BogusControlTransformer.ts +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as crypto from 'crypto'; -import { - factory, - forEachChild, - isBinaryExpression, - isBlock, - isBreakOrContinueStatement, - isClassDeclaration, - isFunctionDeclaration, - isFunctionLike, - isIdentifier, - isLabeledStatement, - isPropertyAccessExpression, - isSourceFile, - isVariableDeclaration, - setParentRecursive, - SyntaxKind, - visitEachChild -} from 'typescript'; - -import type { - BinaryExpression, - BinaryOperator, - Block, - Node, - PropertyAccessExpression, - SourceFile, - Statement, - TransformationContext, - Transformer, - TransformerFactory -} from 'typescript'; - -import type {TransformPlugin} from '../TransformPlugin'; -import type {IOptions} from '../../configs/IOptions'; -import {BogusBlockType} from '../../configs/IBogusControlFlowOption'; -import type {IBogusControlFlowOption} from '../../configs/IBogusControlFlowOption'; -import {NodeUtils} from '../../utils/NodeUtils'; -import {collectExistNames, isObfsIgnoreNode} from '../../utils/TransformUtil'; -import type {AbstractBogusControlHelper} from './AbstractBogusControlHelper'; -import {SimpleBogusControlHelper} from './SimpleBogusControlHelper'; -import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; -import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; -import type {Hash} from 'crypto'; - -namespace secharmony { - const createBogusControlFactory = function (option: IOptions): TransformerFactory { - let profile: IBogusControlFlowOption | undefined = option?.mBogusControlFlow; - if (!profile || !profile.mEnable || profile.mThreshold <= 0) { - return null; - } - - return bogusControlFactory; - - function bogusControlFactory(context: TransformationContext): Transformer { - let blockMap: Map = new Map(); - let blockMapKeys: string[] = []; - let bogusType: BogusBlockType = BogusBlockType.CURRENT_BLOCK_DEFORM; - let sourceFile: SourceFile; - let reservedNames: Set; - let nameGenerator: INameGenerator; - - return transformer; - - function transformer(node: Node): Node { - if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { - return node; - } - - sourceFile = node; - - // we only do bogus control flow with block - if (!hasBlock(node)) { - return node; - } - - reservedNames = collectExistNames(sourceFile); - const options: NameGeneratorOptions = { - reservedNames: reservedNames - }; - nameGenerator = getNameGenerator(NameGeneratorType.ORDERED, options); - - // if bogus block get from other block rename, extract all available blocks - // javascript support current block deform and other block deform, typescript only - // support current block deform. - if (profile.mInsertBlockType === BogusBlockType.OTHER_BLOCK_RENAME && node.fileName.endsWith('.js')) { - bogusType = BogusBlockType.OTHER_BLOCK_RENAME; - getAvailableBlocks(node); - for (const key of blockMap.keys()) { - blockMapKeys.push(key); - } - } - - return setParentRecursive(bogusAst(node), true); - } - - /** - * Block is minimum process unit for us in bogus control flow, - * we only inject code in the most inner Block - * @param node - */ - function bogusAst(node: Node): Node { - if (profile.mSkipLoop && NodeUtils.isLoopStatement(node)) { - return node; - } - - if (!isSourceFile(node) && isObfsIgnoreNode(node, sourceFile)) { - return node; - } - - if (!isBlock(node)) { - return visitEachChild(node, bogusAst, context); - } - - const bogusNode: Block = visitEachChild(node, bogusAst, context); - return bogusControlFlow(bogusNode); - } - - function bogusControlFlow(node: Block): Block { - if (NodeUtils.isContainForbidStringStatement(node) || node.statements.length <= 1) { - return node; - } - - // judge threshold - const randomMaxValue: number = 100; - const temp: number = crypto.randomInt(randomMaxValue); - if (temp > randomMaxValue * profile.mThreshold) { - return node; - } - - let helper: AbstractBogusControlHelper = new SimpleBogusControlHelper( - [...node.statements], - profile.mUseOpaquePredicate, - nameGenerator - ); - - const bogusBlock: Block = getBogusBlock(node, context); - return helper.getBogusStruct(bogusBlock); - } - - /** - * random select other block or deform current block as bogus block - */ - function getBogusBlock(node: Block, context: TransformationContext): Block { - if (bogusType === BogusBlockType.CURRENT_BLOCK_DEFORM || blockMapKeys.length <= 1) { - return deformBlock(node, context); - } - - const randomMaxValue: number = 100; - let index: number = crypto.randomInt(randomMaxValue) % blockMapKeys.length; - if (getHash(NodeUtils.printNode(node, sourceFile)) === blockMapKeys[index]) { - index = (index + 1) % blockMapKeys.length; - } - - let bogusBlock: Block = blockMap.get(blockMapKeys[index]); - // for randomness - const deformedBlock: Block = deformBlock(bogusBlock, context); - // rename identifier - return renameIdentifier(deformedBlock, context, nameGenerator); - } - - /** - * get all blocks of current source file - * @private - */ - function getAvailableBlocks(node: Node): void { - if (!isBlock(node)) { - node.forEachChild((child) => { - getAvailableBlocks(child); - }); - - return; - } - - // remove special statement - let deformedBlock: Block = removeSpecial(node); - if (deformedBlock === null) { - return; - } - - // use printer to print block - blockMap.set(getHash(NodeUtils.printNode(node, sourceFile)), deformedBlock); - node.forEachChild((child) => { - getAvailableBlocks(child); - }); - } - } - }; - - const TRANSFORMER_ORDER: number = 4; - export let transformerPlugin: TransformPlugin = { - 'name': 'BogusControlTransformer', - 'createTransformerFactory': createBogusControlFactory, - 'order': (1 << TRANSFORMER_ORDER) - }; - - const hasBlock = function (node: Node): boolean { - let flag: boolean = false; - let visit = (inputNode): void => { - if (flag) { - return; - } - - if (isBlock(inputNode)) { - flag = true; - return; - } - - forEachChild(inputNode, visit); - }; - - visit(node); - return flag; - }; - - /** - * deform binary expression, example: - * a+b; -> a-b; - * a += b; -> a -= b; - * @param expression - * @private - */ - const deformBinary = function (expression: BinaryExpression): BinaryExpression { - const binaryOperators: SyntaxKind[] = [ - SyntaxKind.PlusToken, SyntaxKind.MinusToken, SyntaxKind.AsteriskToken, - SyntaxKind.SlashToken, SyntaxKind.BarToken, SyntaxKind.CaretToken, - SyntaxKind.AmpersandToken - ]; - - const kind: SyntaxKind = expression.operatorToken.kind; - // plus need consider string value - if (kind === SyntaxKind.PlusToken) { - if (isBinaryExpression(expression.left) || isBinaryExpression(expression.right)) { - return expression; - } - - return factory.createBinaryExpression( - factory.createBinaryExpression( - {...expression.left}, - SyntaxKind.PlusToken, - {...expression.right} - ), - SyntaxKind.PlusToken, - {...expression.left} - ); - } - - if (kind === SyntaxKind.PlusEqualsToken) { - return factory.createBinaryExpression( - {...expression.left}, - SyntaxKind.PlusEqualsToken, - factory.createBinaryExpression( - {...expression.left}, - SyntaxKind.PlusToken, - {...expression.right} - ) - ); - } - - let replaceKind: SyntaxKind = undefined; - if (kind === SyntaxKind.MinusToken || kind === SyntaxKind.AsteriskToken || - kind === SyntaxKind.SlashToken || kind === SyntaxKind.BarToken || - kind === SyntaxKind.CaretToken || kind === SyntaxKind.AmpersandToken || - kind === SyntaxKind.PercentToken) { - const randomMaxValue: number = 100; - let index: number = crypto.randomInt(randomMaxValue) % binaryOperators.length; - if (binaryOperators[index] === expression.operatorToken.kind) { - index = (index + 1) % binaryOperators.length; - } - - replaceKind = binaryOperators[index]; - } - - const binaryEqualOperators: SyntaxKind[] = [SyntaxKind.PlusEqualsToken, SyntaxKind.MinusEqualsToken, - SyntaxKind.AsteriskEqualsToken, SyntaxKind.SlashEqualsToken, SyntaxKind.BarEqualsToken, - SyntaxKind.CaretEqualsToken, SyntaxKind.AmpersandEqualsToken]; - if (kind === SyntaxKind.MinusEqualsToken || kind === SyntaxKind.AsteriskEqualsToken || - kind === SyntaxKind.SlashEqualsToken || kind === SyntaxKind.BarEqualsToken || - kind === SyntaxKind.CaretEqualsToken || kind === SyntaxKind.AmpersandEqualsToken) { - const randomMaxValue: number = 100; - let index: number = crypto.randomInt(randomMaxValue) % binaryEqualOperators.length; - if (binaryEqualOperators[index] === expression.operatorToken.kind) { - index = (index + 1) % binaryEqualOperators.length; - } - - replaceKind = binaryEqualOperators[index]; - } - - const shiftOperators: SyntaxKind[] = [ - SyntaxKind.LessThanLessThanToken, SyntaxKind.GreaterThanGreaterThanToken, - SyntaxKind.GreaterThanGreaterThanGreaterThanToken - ]; - if (shiftOperators.includes(kind)) { - const index: number = (shiftOperators.indexOf(kind) + 1) % shiftOperators.length; - replaceKind = shiftOperators[index]; - } - - const equalOperators: SyntaxKind[] = [ - SyntaxKind.EqualsEqualsToken, SyntaxKind.ExclamationEqualsToken, - SyntaxKind.EqualsEqualsEqualsToken, SyntaxKind.ExclamationEqualsEqualsToken, - SyntaxKind.LessThanToken, SyntaxKind.LessThanEqualsToken, - SyntaxKind.GreaterThanToken, SyntaxKind.GreaterThanEqualsToken, - ]; - if (equalOperators.includes(kind)) { - const index: number = (equalOperators.indexOf(kind) + 1) % equalOperators.length; - replaceKind = equalOperators[index]; - } - - if (replaceKind === undefined) { - return expression; - } - - return factory.createBinaryExpression( - {...expression.left}, - replaceKind as BinaryOperator, - {...expression.right} - ); - }; - - /** - * find special statement: - * return, break, continue, yield, await, super, this - * @param statement - * @private - */ - const findSpecial = function (statement: Statement): boolean { - let result: boolean = false; - let visit = (node: Node): void => { - if (result) { - return; - } - - if (isFunctionLike(node) || - NodeUtils.isLoopStatement(node)) { - return; - } - - if (isBreakOrContinueStatement(node)) { - result = true; - return; - } - - if (node.kind === SyntaxKind.YieldKeyword || - node.kind === SyntaxKind.AwaitKeyword || - node.kind === SyntaxKind.SuperKeyword || - node.kind === SyntaxKind.ThisKeyword) { - result = true; - return; - } - - forEachChild(node, visit); - }; - - visit(statement); - return result; - }; - - /** - * remove special statement of javascript - * @param block - * @private - */ - const removeSpecial = function (block: Block): Block { - const statements: Statement[] = []; - for (const statement of block.statements) { - if (findSpecial(statement)) { - continue; - } - - statements.push(statement); - } - - if (statements.length === 0) { - return null; - } - - return factory.createBlock(statements, true); - }; - - /** - * deform block - * method: - * change binary expression; - * change true and false - * @private - */ - const deformBlock = function (originBlock: Block, context: TransformationContext): Block { - // deform statement - function visit(node: Node): Node { - switch (node.kind) { - case SyntaxKind.PropertyAccessExpression: - return NodeUtils.changePropertyAccessToElementAccess(node as PropertyAccessExpression); - case SyntaxKind.BinaryExpression: - if (NodeUtils.isMostInnerBinary(node)) { - return deformBinary(node as BinaryExpression); - } - break; - case SyntaxKind.TrueKeyword: - return factory.createFalse(); - case SyntaxKind.FalseKeyword: - return factory.createTrue(); - case SyntaxKind.ContinueStatement: - return factory.createBreakStatement(); - default: - break; - } - - return visitEachChild(node, visit, context); - } - - return visit(originBlock) as Block; - }; - - const renameIdentifier = function (originBlock: Block, context: TransformationContext, nameGenerator: INameGenerator): Block { - const nameCache: Map = new Map(); - const labelNameCache: Map = new Map(); - - function visit(node: Node): Node { - if (!isIdentifier(node) || !node.parent) { - return visitEachChild(node, visit, context); - } - - if (isLabeledStatement(node.parent)) { - const deformedName: string = nameGenerator.getName(); - labelNameCache.set(node.text, deformedName); - return factory.createIdentifier(deformedName); - } - - if (isBreakOrContinueStatement(node.parent)) { - const foundLabelName: string = labelNameCache.get(node.text); - if (foundLabelName) { - return factory.createIdentifier(foundLabelName); - } - - return node; - } - - if (isVariableDeclaration(node.parent) || isFunctionDeclaration(node.parent) || isClassDeclaration(node.parent)) { - const deformedName: string = nameGenerator.getName(); - nameCache.set(node.text, deformedName); - return factory.createIdentifier(deformedName); - } - - if (isPropertyAccessExpression(node.parent)) { - return node; - } - - const foundName: string = nameCache.get(node.text); - if (foundName) { - return factory.createIdentifier(foundName); - } - - return node; - } - - return visit(originBlock) as Block; - }; - - /** - * get hash value of string - * @private - */ - const getHash = function (str: string): string { - const hash: Hash = crypto.createHash('sha256'); - return hash.update(str).digest('hex').toLowerCase(); - }; -} - -export = secharmony; diff --git a/arkguard/src/transformers/bogus/SimpleBogusControlHelper.ts b/arkguard/src/transformers/bogus/SimpleBogusControlHelper.ts deleted file mode 100644 index 53f3bdae6c255bd0d4b7359c4d1a20de36f42192..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/bogus/SimpleBogusControlHelper.ts +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import {factory, SyntaxKind} from 'typescript'; -import type {BinaryExpression, Block, Expression, IfStatement, Statement} from 'typescript'; -import crypto from 'crypto'; - -import {AbstractBogusControlHelper} from './AbstractBogusControlHelper'; -import {NodeUtils} from '../../utils/NodeUtils'; -import type {INameGenerator} from '../../generator/INameGenerator'; - -export class SimpleBogusControlHelper extends AbstractBogusControlHelper { - protected mNameGenerator: INameGenerator; - - public constructor(units: Statement[], useOpaquePredicate: boolean, nameGenerator: INameGenerator) { - super(units, useOpaquePredicate); - this.mNameGenerator = nameGenerator; - } - - public getNewBlock(bogusBlock: Block): Block { - const preStatements: Statement[] = []; - const randomMaxValue: number = 100; - const useTrue: boolean = (crypto.randomInt(randomMaxValue) & 1) === 0; - - let predicate: Expression; - if (this.mUseOpaquePredicate) { - predicate = this.createOpaquePredicate(preStatements, useTrue); - } else { - predicate = this.createSimplePredicate(preStatements, useTrue); - } - - const originalBlock: Block = factory.createBlock([...this.mOriginalUnits], true); - - let ifStatement: IfStatement; - if (useTrue) { - ifStatement = factory.createIfStatement(predicate, originalBlock, bogusBlock); - } else { - ifStatement = factory.createIfStatement(predicate, bogusBlock, originalBlock); - } - - return factory.createBlock( - [ - ...preStatements, - ifStatement - ], - true - ); - } - - public createSimplePredicate(preStatements: Statement[], useTrue: boolean): Expression { - const arrayName: string = this.mNameGenerator.getName(); - const stringArray: string[] = []; - const traversalRange: number = 10; - for (let i = 0; i < traversalRange; i++) { - stringArray.push(this.mNameGenerator.getName()); - } - - const arrayInitStatement: Statement = NodeUtils.createArrayInit(true, arrayName, - SyntaxKind.StringLiteral, stringArray); - preStatements.push(arrayInitStatement); - - const syntaxSymbol: SyntaxKind = useTrue ? SyntaxKind.ExclamationEqualsEqualsToken : - SyntaxKind.EqualsEqualsEqualsToken; - - return factory.createBinaryExpression( - factory.createElementAccessExpression( - factory.createIdentifier(arrayName), - factory.createNumericLiteral('1') - ), - syntaxSymbol, - factory.createElementAccessExpression( - factory.createIdentifier(arrayName), - factory.createNumericLiteral('6') - ) - ); - } - - /** - * create condition judgement use opaque predicate - */ - public createOpaquePredicate(preStatements, useTrue: boolean): Expression { - const nameGenerator: INameGenerator = this.mNameGenerator; - - const xName: string = nameGenerator.getName(); - const randomMaxValue: number = 125; - preStatements.push(NodeUtils.createNumericWithRandom(xName, 1, randomMaxValue)); - - /** - * y < 10 || x * (x + 1) % 2 == 0, always true - * x is integer - */ - function method1(): BinaryExpression { - const yName: string = nameGenerator.getName(); - preStatements.push(NodeUtils.createNumericWithRandom(yName, 1, randomMaxValue)); - - const left: BinaryExpression = factory.createBinaryExpression( - factory.createIdentifier(yName), - SyntaxKind.LessThanToken, - factory.createNumericLiteral('10') - ); - - const rightLeft: BinaryExpression = factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createIdentifier(xName), - SyntaxKind.AsteriskToken, - factory.createParenthesizedExpression( - factory.createBinaryExpression( - factory.createIdentifier(xName), - SyntaxKind.PlusToken, - factory.createNumericLiteral('1') - ) - ) - ), - SyntaxKind.PercentToken, - factory.createNumericLiteral('2') - ); - - const right: BinaryExpression = factory.createBinaryExpression( - rightLeft, - SyntaxKind.EqualsEqualsEqualsToken, - factory.createNumericLiteral('0') - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.BarBarToken, - right - ); - } - - /** - * 7* x* x − y* y != 1 || y < n, always true - * x, y in [0, 125); - * n in [0, 125]; - */ - function method2(): BinaryExpression { - const yName: string = nameGenerator.getName(); - const randomMaxValue: number = 125; - preStatements.push(NodeUtils.createNumericWithRandom(yName, 1, randomMaxValue)); - - const nName: string = nameGenerator.getName(); - preStatements.push(NodeUtils.createNumericWithRandom(nName, 0, randomMaxValue)); - - const left: BinaryExpression = factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createNumericLiteral('7'), - SyntaxKind.AsteriskToken, - factory.createIdentifier(xName) - ), - SyntaxKind.AsteriskToken, - factory.createIdentifier(xName) - ), - SyntaxKind.MinusToken, - factory.createBinaryExpression( - factory.createIdentifier(yName), - SyntaxKind.AsteriskToken, - factory.createIdentifier(yName) - ) - ), - SyntaxKind.ExclamationEqualsEqualsToken, - factory.createNumericLiteral('1') - ); - - const right: BinaryExpression = factory.createBinaryExpression( - factory.createIdentifier(yName), - SyntaxKind.LessThanToken, - factory.createIdentifier(nName) - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.BarBarToken, - right - ); - } - - /** - * (4*x*x + 1) % 19 != 0, always true - */ - function method3(): BinaryExpression { - const leftInner: BinaryExpression = factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createNumericLiteral('4'), - SyntaxKind.AsteriskToken, - factory.createIdentifier(xName) - ), - SyntaxKind.AsteriskToken, - factory.createIdentifier(xName) - ), - SyntaxKind.PlusToken, - factory.createNumericLiteral('4') - ); - - const left: BinaryExpression = factory.createBinaryExpression( - factory.createParenthesizedExpression( - leftInner - ), - SyntaxKind.PercentToken, - factory.createNumericLiteral('19') - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.ExclamationEqualsEqualsToken, - factory.createNumericLiteral('0') - ); - } - - /** - * (x*x + x +7) % 81 != 0, always true - */ - function method4(): BinaryExpression { - const leftInner: BinaryExpression = factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createIdentifier(xName), - SyntaxKind.AsteriskToken, - factory.createIdentifier(xName) - ), - SyntaxKind.PlusToken, - factory.createIdentifier(xName) - ), - SyntaxKind.PlusToken, - factory.createNumericLiteral('7') - ); - - const left: BinaryExpression = factory.createBinaryExpression( - factory.createParenthesizedExpression( - leftInner - ), - SyntaxKind.PercentToken, - factory.createNumericLiteral('81') - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.ExclamationEqualsEqualsToken, - factory.createNumericLiteral('0') - ); - } - - /** - * (x*x*x -x) % 3 == 0, always true - */ - function method5(): BinaryExpression { - const leftInner: BinaryExpression = factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createIdentifier(xName), - SyntaxKind.AsteriskToken, - factory.createIdentifier(xName) - ), - SyntaxKind.AsteriskToken, - factory.createIdentifier(xName) - ), - SyntaxKind.MinusToken, - factory.createIdentifier(xName) - ); - - const left: BinaryExpression = factory.createBinaryExpression( - factory.createParenthesizedExpression( - leftInner - ), - SyntaxKind.PercentToken, - factory.createNumericLiteral('3') - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.EqualsEqualsEqualsToken, - factory.createNumericLiteral('0') - ); - } - - const methodList: (() => BinaryExpression)[] = [method1, method2, method3, method4, method5]; - const opaqueMethod: () => BinaryExpression = methodList[crypto.randomInt(methodList.length)]; - - if (useTrue) { - return opaqueMethod(); - } - - return factory.createPrefixUnaryExpression( - SyntaxKind.ExclamationToken, - factory.createParenthesizedExpression( - opaqueMethod() - ) - ); - } -} diff --git a/arkguard/src/transformers/control/AbstractControlFlowFlattenHelper.ts b/arkguard/src/transformers/control/AbstractControlFlowFlattenHelper.ts deleted file mode 100644 index c8533b76c0c25fcfe1bd39c78205a8e874d99d10..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/control/AbstractControlFlowFlattenHelper.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type {Expression, ForStatement, Statement, SwitchStatement, WhileStatement} from 'typescript'; - -import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; -import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; - -export abstract class AbstractControlFlowFlattenHelper { - protected mOrderObjName: string; - - protected mIndexName: string; - - protected mOriginalUnits: Statement[]; - - protected mStatementUnits: Map; - - protected mNameGenerator: INameGenerator; - - protected constructor(units: Statement[], reservedNames: Set) { - this.mOriginalUnits = units; - const options: NameGeneratorOptions = { - reservedNames: reservedNames - }; - this.mNameGenerator = getNameGenerator(NameGeneratorType.ORDERED, options); - this.mOrderObjName = this.mNameGenerator.getName(); - reservedNames.add(this.mOrderObjName); - this.mIndexName = this.mNameGenerator.getName(); - reservedNames.add(this.mIndexName); - } - - public abstract getLoopCondition(): Expression; - - public abstract getLoopStruct(): WhileStatement | ForStatement; - - public abstract getSwitchStruct(): SwitchStatement; - - public abstract getVariableRelatedStatements(): Statement[]; - - public getFlattenStruct(): Statement[] { - return [...this.getVariableRelatedStatements(), this.getLoopStruct()]; - } -} diff --git a/arkguard/src/transformers/control/ConfusedCharsFlattenHelper.ts b/arkguard/src/transformers/control/ConfusedCharsFlattenHelper.ts deleted file mode 100644 index c608513768ea4053badf8465332654c506c3f2da..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/control/ConfusedCharsFlattenHelper.ts +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - factory, - isIdentifierStart, - isReturnStatement, - ScriptTarget, - SyntaxKind, - NodeFlags -} from 'typescript'; - -import type { - CaseClause, - ObjectLiteralExpression, - PropertyAssignment, - Statement, - SwitchStatement, - CallExpression, - VariableStatement, - ElementAccessExpression -} from 'typescript'; - -import {randomInt} from 'crypto'; - -import {table as confusionTable} from '../../configs/preset/ConfusionTables'; -import {SimpleControlFlowFlattenHelper} from './SimpleControlFlowFlattenHelper'; - -export class ConfusedCharsFlattenHelper extends SimpleControlFlowFlattenHelper { - private readonly mChoiceName: string; - - private mMangledTable: Map; - - private mChooseMaps: Map; - - public constructor(units: Statement[], reservedNames: Set) { - super(units, reservedNames); - - this.mChoiceName = this.mNameGenerator.getName(); - reservedNames.add(this.mChoiceName); - - this.mMangledTable = new Map(); - this.mChooseMaps = new Map(); - - let index: number = 0; - const confusionTableKeys: string[] = Object.keys(confusionTable); - for (const key of confusionTableKeys) { - this.mMangledTable.set(index++, confusionTable[key]); - } - - index = 0; - let chooseList: string[] = this.chooseMangledChars(); - for (const [key, _] of this.mStatementUnits) { - this.mChooseMaps.set(key, chooseList[index++]); - } - } - - private chooseMangledChars(): string[] { - let chooseList: Set = new Set(); - let remainLen: number = this.mOriginalUnits.length; - let historyIndex: Set = new Set(); - - while (remainLen > 0) { - if (historyIndex.size === this.mMangledTable.size) { - const MIN_UNICODE = 0x100; - const MAX_UNICODE = 0x7fff; - let unicode: number = randomInt(MIN_UNICODE, MAX_UNICODE); - if (isIdentifierStart(unicode, ScriptTarget.ES2015)) { - chooseList.add(String.fromCharCode(unicode)); - remainLen = this.mOriginalUnits.length - chooseList.size; - } - - continue; - } - - let choice: number = randomInt(0, this.mMangledTable.size); - if (historyIndex.has(choice)) { - continue; - } - - historyIndex.add(choice); - let chars: string[] = this.mMangledTable.get(choice).filter((ch) => { - return isIdentifierStart(ch.codePointAt(0), ScriptTarget.ES2015); - }); - - let len: number = chars.length > remainLen ? remainLen : chars.length; - chars.slice(0, len).forEach((ch) => { - chooseList.add(ch); - }); - - remainLen = this.mOriginalUnits.length - chooseList.size; - } - - return Array.from(chooseList); - } - - public getVariableRelatedStatements(): Statement[] { - let properties: PropertyAssignment[] = []; - this.mChooseMaps.forEach((val, _) => { - const RANDOM_MIN = 0; - const RANDOM_MAX = 36; - const propValue = randomInt(RANDOM_MIN, RANDOM_MAX); - let prop: PropertyAssignment = factory.createPropertyAssignment(val, factory.createNumericLiteral(propValue)); - properties.push(prop); - }); - - let literal: ObjectLiteralExpression = factory.createObjectLiteralExpression(properties); - - const choiceExpression: CallExpression = factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('Object'), - factory.createIdentifier('keys') - ), - undefined, - [factory.createIdentifier(this.mOrderObjName)] - ); - - const variableStatement: VariableStatement = factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration(this.mOrderObjName, undefined, undefined, literal), - factory.createVariableDeclaration(this.mIndexName, undefined, undefined, factory.createNumericLiteral(0)), - factory.createVariableDeclaration(this.mChoiceName, undefined, undefined, choiceExpression) - ], - NodeFlags.Let - ) - ); - - return [variableStatement]; - } - - public getSwitchStruct(): SwitchStatement { - let condition: ElementAccessExpression = factory.createElementAccessExpression( - factory.createIdentifier(this.mChoiceName), - factory.createPostfixUnaryExpression(factory.createIdentifier(this.mIndexName), SyntaxKind.PlusPlusToken) - ); - - let caseList: CaseClause[] = []; - for (let index = 0; index < this.mOriginalUnits.length; index++) { - let st: Statement = this.mStatementUnits.get(index); - let statements: Statement[] = isReturnStatement(st) ? [st] : [st, factory.createContinueStatement()]; - let caseSt: CaseClause = factory.createCaseClause( - factory.createStringLiteral(this.mChooseMaps.get(index)), statements); - caseList.push(caseSt); - } - - return factory.createSwitchStatement(condition, factory.createCaseBlock(caseList)); - } -} diff --git a/arkguard/src/transformers/control/ControlFlowFlattenTransformer.ts b/arkguard/src/transformers/control/ControlFlowFlattenTransformer.ts deleted file mode 100644 index 97728f22adb4b33211d25094294f378c63ba69fa..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/control/ControlFlowFlattenTransformer.ts +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - factory, - forEachChild, - isBlock, - isBreakOrContinueStatement, isCallExpression, - isClassDeclaration, isExpressionStatement, - isFunctionDeclaration, - isFunctionLike, isSourceFile, isStringLiteral, - isSwitchStatement, - isVariableStatement, - NodeFlags, - setParentRecursive, - SyntaxKind, - visitEachChild -} from 'typescript'; - -import type { - Block, - Node, - SourceFile, - Statement, - TransformationContext, - Transformer, - TransformerFactory -} from 'typescript'; - -import crypto from 'crypto'; - -import type {TransformPlugin} from '../TransformPlugin'; -import type {IOptions} from '../../configs/IOptions'; -import type {IControlFlowFatteningOption} from '../../configs/IControlFlowFatteningOption'; -import {SimpleControlFlowFlattenHelper} from './SimpleControlFlowFlattenHelper'; -import {ConfusedCharsFlattenHelper} from './ConfusedCharsFlattenHelper'; -import {NodeUtils} from '../../utils/NodeUtils'; -import {collectExistNames, isObfsIgnoreNode} from '../../utils/TransformUtil'; - -namespace secharmony { - - const MIN_TARGET_BLOCK_LENGTH: number = 4; - const MAX_TARGET_BLOCK_LENGTH: number = 1000; - - const createCfgFlattenFactory = function (option: IOptions): TransformerFactory { - let profile: IControlFlowFatteningOption | undefined = option?.mControlFlowFlattening; - if (!profile || !profile.mEnable) { - return null; - } - - return cfgFlattenFactory; - - function cfgFlattenFactory(context: TransformationContext): Transformer { - let narrowNames: string[] = option?.mNarrowFunctionNames ?? []; - let threshold: number = profile?.mThreshold; - let skipLoop: boolean = profile.mSkipLoop; - let reservedNames: Set; - let sourceFile: SourceFile; - - return controlFlowFlattenTransformer; - - function controlFlowFlattenTransformer(node: Node): Node { - if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { - return node; - } - - sourceFile = node; - reservedNames = collectExistNames(node); - - return setParentRecursive(controlFlowFlatten(node), true); - } - - function controlFlowFlatten(node: Node): Node { - if (skipLoop && NodeUtils.isLoopStatement(node)) { - return node; - } - - if (!isSourceFile(node) && isObfsIgnoreNode(node, sourceFile)) { - return node; - } - - if (!isBlock(node)) { - return visitEachChild(node, controlFlowFlatten, context); - } - - let newNode: Block = visitEachChild(node, controlFlowFlatten, context); - if (ignoreFlatten(newNode.statements.length) || - NodeUtils.isContainNarrowNames(node, narrowNames)) { - return newNode; - } - - return factory.createBlock(obfuscateCfg(newNode), true); - } - - function obfuscateCfg(node: Block): Statement[] { - let finalStatements: Statement[] = []; - const continuousStatement: Statement[] = []; - - // 1. filter continuous statements that can be flattened - node.statements.forEach((child) => { - if (!isForbiddenStatement(child)) { - continuousStatement.push(child); - return; - } - - if (ignoreFlatten(continuousStatement.length)) { - finalStatements = [...finalStatements, ...continuousStatement]; - finalStatements.push(child); - continuousStatement.length = 0; - return; - } - - // 2. flatten continuous statements - let helper: SimpleControlFlowFlattenHelper = profile?.mAdvance ? - new ConfusedCharsFlattenHelper(continuousStatement, reservedNames) : - new SimpleControlFlowFlattenHelper(continuousStatement, reservedNames); - - const flattenStatements: Statement[] = helper.getFlattenStruct(); - finalStatements = [...finalStatements, ...flattenStatements]; - finalStatements.push(child); - continuousStatement.length = 0; - }); - - if (ignoreFlatten(continuousStatement.length)) { - finalStatements = [...finalStatements, ...continuousStatement]; - continuousStatement.length = 0; - return finalStatements; - } - - // 2. flatten continuous statements - let finalHelper: SimpleControlFlowFlattenHelper = profile?.mAdvance ? - new ConfusedCharsFlattenHelper(continuousStatement, reservedNames) : - new SimpleControlFlowFlattenHelper(continuousStatement, reservedNames); - - const flatten: Statement[] = finalHelper.getFlattenStruct(); - finalStatements = [...finalStatements, ...flatten]; - return finalStatements; - } - - function ignoreFlatten(statementsLen: number): boolean { - if (statementsLen < MIN_TARGET_BLOCK_LENGTH || statementsLen > MAX_TARGET_BLOCK_LENGTH) { - return true; - } - - // judge threshold - const randomMaxValue: number = 100; - const temp: number = crypto.randomInt(randomMaxValue); - return temp > randomMaxValue * threshold; - } - - /** - * is break or continue statement contained - * @param statement - */ - function isContainForbidBreakOrContinue(statement: Statement): boolean { - let result: boolean = false; - let visit = (n: Node): void => { - if (isFunctionLike(n) || - isSwitchStatement(n) || - NodeUtils.isLoopStatement(n)) { - return; - } - - if (isBreakOrContinueStatement(n)) { - result = true; - return; - } - - forEachChild(n, visit); - }; - - forEachChild(statement, visit); - return result; - } - - /** - * is statement forbidden in control flow flatten, list of forbidden: - * - let/const declaration; - * - function declaration; - * - class declaration; - * - 'use strict' like statement; - * - break/continue; - * @param statement - */ - function isForbiddenStatement(statement: Statement): boolean { - if (isVariableStatement(statement)) { - return !!(statement.declarationList.flags & NodeFlags.Const || - statement.declarationList.flags & NodeFlags.Let); - } - - if (isExpressionStatement(statement)) { - if (isStringLiteral(statement.expression)) { - return true; - } - - return isCallExpression(statement.expression) && - statement.expression.expression.kind === SyntaxKind.SuperKeyword; - } - - return isFunctionDeclaration(statement) || - isClassDeclaration(statement) || - isContainForbidBreakOrContinue(statement); - } - } - }; - - const TRANSFORMER_ORDER: number = 7; - export let transformerPlugin: TransformPlugin = { - 'name': 'ControlFlowFlattenTransformer', - 'createTransformerFactory': createCfgFlattenFactory, - 'order': (1 << TRANSFORMER_ORDER) - }; -} - -export = secharmony; diff --git a/arkguard/src/transformers/control/SimpleControlFlowFlattenHelper.ts b/arkguard/src/transformers/control/SimpleControlFlowFlattenHelper.ts deleted file mode 100644 index 0a8eab91e77907cf5df95397e29c5cc5bac1ea09..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/control/SimpleControlFlowFlattenHelper.ts +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - factory, - isReturnStatement, - Map, - NodeFlags, - SyntaxKind, -} from 'typescript'; - -import type { - Block, - CaseClause, - ElementAccessExpression, - Expression, - ForStatement, - NumericLiteral, - Statement, - SwitchStatement, - WhileStatement -} from 'typescript'; - -import crypto from 'crypto'; - -import {AbstractControlFlowFlattenHelper} from './AbstractControlFlowFlattenHelper'; -import {ListUtil} from '../../utils/ListUtil'; - -export class SimpleControlFlowFlattenHelper extends AbstractControlFlowFlattenHelper { - protected mIndexArrayName: string; - protected mStringArray: number[]; - protected mIndexArray: number[]; - - public constructor(units: Statement[], reservedNames: Set) { - super(units, reservedNames); - - this.mIndexArrayName = this.mNameGenerator.getName(); - reservedNames.add(this.mIndexArrayName); - - let shuffledArr: number[] = ListUtil.getInitList(units.length); - ListUtil.shuffle(shuffledArr); - - this.mStringArray = [...shuffledArr]; - ListUtil.shuffle(this.mStringArray); - - this.mIndexArray = []; - shuffledArr.forEach((value) => { - this.mIndexArray.push(this.mStringArray.indexOf(value)); - }); - - this.mStatementUnits = new Map(); - let index: number = 0; - shuffledArr.forEach((val) => { - this.mStatementUnits.set(val, this.mOriginalUnits[index++]); - }); - } - - public getSwitchStruct(): SwitchStatement { - let condition: ElementAccessExpression = factory.createElementAccessExpression( - factory.createIdentifier(this.mOrderObjName), - factory.createElementAccessExpression( - factory.createIdentifier(this.mIndexArrayName), - factory.createPostfixUnaryExpression( - factory.createIdentifier(this.mIndexName), - SyntaxKind.PlusPlusToken - ) - )); - - let caseList: CaseClause[] = []; - for (let index = 0; index < this.mOriginalUnits.length; index++) { - let st: Statement = this.mStatementUnits.get(index); - let statements: Statement[] = isReturnStatement(st) ? [st] : [st, factory.createContinueStatement()]; - let caseSt: CaseClause = factory.createCaseClause( - factory.createStringLiteral(index.toString()), statements); - caseList.push(caseSt); - } - - return factory.createSwitchStatement(condition, factory.createCaseBlock(caseList)); - } - - public getLoopStruct(): WhileStatement | ForStatement { - let loopBody: Block = factory.createBlock([ - this.getSwitchStruct(), - factory.createBreakStatement(), - ]); - - const MAX_RANDOM = 100; - const HALF_RANDOM = 100; - const temp: number = crypto.randomInt(MAX_RANDOM); - let choice: boolean = temp > HALF_RANDOM; - if (choice) { - return factory.createForStatement(undefined, undefined, undefined, loopBody); - } - - let condition: Expression = this.getLoopCondition(); - return factory.createWhileStatement(condition, loopBody); - } - - public getLoopCondition(): Expression { - return factory.createPrefixUnaryExpression( - SyntaxKind.ExclamationToken, - factory.createPrefixUnaryExpression( - SyntaxKind.ExclamationToken, - factory.createArrayLiteralExpression([]) - ) - ); - } - - public getVariableRelatedStatements(): Statement[] { - let indexStr: string = this.mStringArray.join('|'); - - let arrayList: NumericLiteral[] = []; - this.mIndexArray.forEach((value) => { - arrayList.push(factory.createNumericLiteral(value.toString())); - }); - - return [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier(this.mOrderObjName), - undefined, - undefined, - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createStringLiteral(indexStr), - factory.createIdentifier('split') - ), - undefined, - [factory.createStringLiteral('|')] - ) - ), - factory.createVariableDeclaration( - factory.createIdentifier(this.mIndexArrayName), - undefined, - undefined, - factory.createArrayLiteralExpression(arrayList) - ), - factory.createVariableDeclaration( - factory.createIdentifier(this.mIndexName), - undefined, - undefined, - factory.createNumericLiteral(0) - ) - ], - NodeFlags.Let - ) - ) - ]; - } -} diff --git a/arkguard/src/transformers/data/DataObfuscationTransformer.ts b/arkguard/src/transformers/data/DataObfuscationTransformer.ts deleted file mode 100644 index 9393ab6ba36551e49c97dc8e92a8ef07b9a7462d..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/data/DataObfuscationTransformer.ts +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - factory, isElementAccessExpression, - isEnumDeclaration, isExportDeclaration, - isExpressionStatement, isImportDeclaration, - isImportEqualsDeclaration, - isSourceFile, - isStringLiteralLike, isTypeAliasDeclaration, - setParentRecursive, - visitEachChild, -} from 'typescript'; - -import type { - Node, - SourceFile, - Statement, - TransformationContext, - Transformer, - TransformerFactory -} from 'typescript'; - -import crypto from 'crypto'; - -import type { - IBooleanOption, - IDataObfuscationOption, - INumberOption, - IStringOption -} from '../../configs/IDataObfuscationOption'; - -import type {TransformPlugin} from '../TransformPlugin'; -import type {IOptions} from '../../configs/IOptions'; -import {SimpleStringObfuscateHelper} from './SimpleStringObfuscateHelper'; -import {NodeUtils} from '../../utils/NodeUtils'; -import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; -import {collectExistNames, isObfsIgnoreNode} from '../../utils/TransformUtil'; -import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; -import {BoolObfuscationHelper} from './BoolObfuscationHelper'; -import {NumberObfuscationHelper} from './NumberObfuscationHelper'; - -/** - * Data obfuscation must follow attribute name obfuscation. Because if data is obfuscated and strings are - * extracted onto arrays, attribute name obfuscation may result in some strings not being obfuscated. - */ -namespace secharmony { - const RANDOM_MAX: number = 100; - - const createDataObfuscationFactory = function (options: IOptions): TransformerFactory { - let profile: IDataObfuscationOption | undefined = options?.mDataObfuscation; - if (!profile || !profile.mEnable) { - return null; - } - - return dataObfuscationFactory; - - function dataObfuscationFactory(context: TransformationContext): Transformer { - let boolOption: IBooleanOption = profile.mBooleanOption; - let stringOption: IStringOption = profile.mStringOption; - let numberOption: INumberOption = profile.mNumberOption; - let nameGenerator: INameGenerator; - let sourceFile: SourceFile; - - return transformer; - - function transformer(node: Node): Node { - if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { - return node; - } - - sourceFile = node; - let newNode: SourceFile = node; - - if (boolOption && boolOption.mEnable) { - newNode = doBoolTransform(newNode) as SourceFile; - } - - if (numberOption && numberOption.mEnable) { - newNode = doNumberTransform(newNode) as SourceFile; - } - - if (stringOption && stringOption.mEnable) { - const reservedNames: Set = collectExistNames(node); - const generatorOptions: NameGeneratorOptions = { - reservedNames: reservedNames - }; - - nameGenerator = getNameGenerator(NameGeneratorType.ORDERED, generatorOptions); - newNode = doStringTransform(newNode, nameGenerator); - } - - return newNode; - } - - function doBoolTransform(node: Node): Node { - if (boolOption.mSkipLoop && NodeUtils.isLoopStatement(node)) { - return node; - } - - if (!BoolObfuscationHelper.isBooleanLiteral(node)) { - return visitEachChild(node, doBoolTransform, context); - } - - // threshold check - const temp: number = crypto.randomInt(RANDOM_MAX); - if (temp > RANDOM_MAX * boolOption.mThreshold) { - return node; - } - - if (BoolObfuscationHelper.isTrueKeyword(node)) { - return BoolObfuscationHelper.createTrueObfuscation(); - } - - return BoolObfuscationHelper.createFalseObfuscation(); - } - - function doNumberTransform(node: Node): Node { - if (numberOption.mSkipLoop && NodeUtils.isLoopStatement(node)) { - return node; - } - - if (!NumberObfuscationHelper.isTargetNumberNode(node)) { - return visitEachChild(node, doNumberTransform, context); - } - - // threshold check - const temp: number = crypto.randomInt(RANDOM_MAX); - if (temp > RANDOM_MAX * numberOption.mThreshold) { - return node; - } - - return NumberObfuscationHelper.convertNumberToExpression(node); - } - - function doStringTransform(node: SourceFile, generator: INameGenerator): SourceFile { - let helper: SimpleStringObfuscateHelper = new SimpleStringObfuscateHelper(stringOption, generator); - helper.collectLiterals(node); - - sourceFile = node as SourceFile; - let source: Node = stringVisitor(node); - let newStatements: Statement[] = NodeUtils.randomInsertStatements( - NodeUtils.randomInsertStatements([...(source as SourceFile).statements], - helper.prepareIndexFunctionStruct()), - helper.prepareArrayFunctionStruct()); - - return setParentRecursive(factory.updateSourceFile(source as SourceFile, newStatements), true); - - function stringVisitor(node: Node): Node { - if (stringOption.mSkipLoop && NodeUtils.isLoopStatement(node)) { - return node; - } - - if (!isSourceFile(node) && isObfsIgnoreNode(node, sourceFile)) { - return node; - } - - // module name in import / export like statement - if ((isImportDeclaration(node) || isExportDeclaration(node)) && node.moduleSpecifier) { - return node; - } - - if (isImportEqualsDeclaration(node)) { - return node; - } - - if (isTypeAliasDeclaration(node)) { - return node; - } - - // TS18033: Only numeric enums can have computed members, but this expression has type 'string'. If - // you do not need exhaustiveness checks, consider using an object literal instead. - if (isEnumDeclaration(node)) { - return node; - } - - if (!isStringLiteralLike(node)) { - return visitEachChild(node, stringVisitor, context); - } - - if (isExpressionStatement(node.parent)) { - return node; - } - - if (stringOption.mSkipProperty && isElementAccessExpression(node.parent)) { - return node; - } - - if (stringOption.mReservedStrings && stringOption.mReservedStrings.includes(node.text)) { - return node; - } - - if (!helper.isTargetStr(node.text)) { - return node; - } - - if (!NodeUtils.isExtractableString(node)) { - return visitEachChild(node, stringVisitor, context); - } - - let prob: number = Math.random(); - if (prob > stringOption.mThreshold) { - return node; - } - - return helper.prepareReplaceStruct(node); - } - } - } - }; - - const TRANSFORMER_ORDER: number = 8; - export let transformerPlugin: TransformPlugin = { - 'name': 'Data Obfuscation', - 'order': (1 << TRANSFORMER_ORDER), - 'createTransformerFactory': createDataObfuscationFactory - }; -} - -export = secharmony; diff --git a/arkguard/src/transformers/data/NumberObfuscationHelper.ts b/arkguard/src/transformers/data/NumberObfuscationHelper.ts deleted file mode 100644 index 33063625ce74858cf236ba9984d83f9ad92edfe3..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/data/NumberObfuscationHelper.ts +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import {factory, isNumericLiteral, isPropertyAssignment, SyntaxKind} from 'typescript'; -import type {BinaryExpression, Node} from 'typescript'; -import {randomInt} from 'crypto'; - -export class NumberObfuscationHelper { - /** - * ignore number like property in object - * let xxx = { - * 0: 13-1, - * 1: 1212, - * } - * - * @param node - */ - public static isTargetNumberNode(node: Node): boolean { - if (!isNumericLiteral(node)) { - return false; - } - - if (node.parent && isPropertyAssignment(node.parent)) { - return node.parent.name !== node; - } - - return true; - } - - public static convertNumberToExpression(node: Node): Node { - if (!isNumericLiteral(node)) { - return node; - } - - const originNumber: number = Number(node.text); - if (this.isUnsafeNumber(originNumber)) { - return node; - } - - const [intPart, decimalPart] = this.extractIntegerAndDecimalParts(originNumber); - - // split intPart - const MIN_RANDOM = 0xff; - const MAX_RIGHT_RANDOM = 0x1fff; - const randomLeft: number = randomInt(MIN_RANDOM, MAX_RIGHT_RANDOM); - const randomRight: number = intPart - randomLeft; - - const MAX_LEFT_LEFT_RANDOM = 0xfff; - const randomLeftLeft: number = randomInt(MIN_RANDOM, MAX_LEFT_LEFT_RANDOM); - const randomLeftRight: number = randomLeft - randomLeftLeft; - - const leftPartExpression: BinaryExpression = factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createNumericLiteral(this.toHexString(randomLeftLeft)), - SyntaxKind.BarToken, - factory.createNumericLiteral(this.toHexString(randomLeftRight)) - ), - SyntaxKind.PlusToken, - factory.createBinaryExpression( - factory.createNumericLiteral(this.toHexString(randomLeftLeft)), - SyntaxKind.AmpersandToken, - factory.createNumericLiteral(this.toHexString(randomLeftRight)) - ) - ); - - const intPartExpression: BinaryExpression = factory.createBinaryExpression( - leftPartExpression, - SyntaxKind.PlusToken, - factory.createNumericLiteral(this.toHexString(randomRight)) - ); - - if (decimalPart) { - return factory.createParenthesizedExpression( - factory.createBinaryExpression( - intPartExpression, - SyntaxKind.PlusToken, - factory.createNumericLiteral(decimalPart) - )); - } - - return factory.createParenthesizedExpression(intPartExpression); - } - - public static extractIntegerAndDecimalParts(number: number): [number, number | null] { - const integerPart: number = Math.trunc(number); - const decimalPart: number | null = number !== integerPart ? number % 1 : null; - - return [integerPart, decimalPart]; - } - - public static isUnsafeNumber(number: number): boolean { - if (isNaN(number)) { - return true; - } - - return number < Number.MIN_SAFE_INTEGER || number > Number.MAX_SAFE_INTEGER; - } - - public static toHexString(value: number): string { - const HEX_RADIX = 16; - if (value > 0) { - return '0x' + value.toString(HEX_RADIX); - } - - const absValue: number = Math.abs(value); - return '-0x' + absValue.toString(HEX_RADIX); - } -} diff --git a/arkguard/src/transformers/data/SimpleStringObfuscateHelper.ts b/arkguard/src/transformers/data/SimpleStringObfuscateHelper.ts deleted file mode 100644 index e4eda5c80216206b4b474319540cd88858109159..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/data/SimpleStringObfuscateHelper.ts +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - factory, - forEachChild, - isStringLiteralLike, - NodeFlags, -} from 'typescript'; - -import type { - CallExpression, - Expression, - FunctionDeclaration, - Node, - ParameterDeclaration, - ReturnStatement, - SourceFile, Statement, - StringLiteralLike, - VariableDeclaration, - VariableStatement -} from 'typescript'; - -import {createStringUnit, EncryptType} from './StringUnit'; -import type {StringUnit} from './StringUnit'; -import type {IStringOption} from '../../configs/IDataObfuscationOption'; -import {Base64Helper} from '../../utils/EncryptedUtils'; -import {NodeUtils} from '../../utils/NodeUtils'; -import type {INameGenerator} from '../../generator/INameGenerator'; - -export class SimpleStringObfuscateHelper { - private stringUnits: Map; - - private stringArray: string[]; - - private profile: IStringOption; - - private readonly arrayFuncName: string; - - private readonly indexFuncName: string; - - private readonly mNameGenerator: INameGenerator; - - public constructor(option: IStringOption, generator: INameGenerator) { - this.profile = option; - this.stringUnits = new Map(); - this.stringArray = []; - this.mNameGenerator = generator; - this.arrayFuncName = this.mNameGenerator.getName(); - this.indexFuncName = this.mNameGenerator.getName(); - } - - public collectLiterals(sourceFile: SourceFile): void { - let visit = (node: Node): void => { - if (!isStringLiteralLike(node)) { - forEachChild(node, visit); - return; - } - - // filter octal encode string - let code: string = NodeUtils.printNode(node, sourceFile); - const MIN_OCTAL_LEN: number = 3; - const ZERO_INDEX = 2; - if (code.length >= MIN_OCTAL_LEN && code[1].startsWith('\\') && code[ZERO_INDEX].startsWith('0')) { - return; - } - - // extract all - let content: string = node.text; - if (!content) { - return; - } - - if (this.stringUnits.has(content)) { - let unit: StringUnit = this.stringUnits.get(content); - unit.nodeList.push(node); - } else { - let unit: StringUnit = createStringUnit(node); - if (this.profile.mEncryptType === EncryptType.BASE64) { - let encrypted: string = new Base64Helper().encode(content); - if (encrypted) { - unit.encryptContent = encrypted; - } else { - return; - } - } - - this.stringArray.push(unit.encryptContent); - this.stringUnits.set(content, unit); - } - - forEachChild(node, visit); - }; - - visit(sourceFile); - } - - public prepareReplaceStruct(literal: StringLiteralLike): Node { - let stringUnit: StringUnit = this.stringUnits.get(literal.text); - if (!stringUnit) { - return literal; - } - - let index: number = this.stringArray.indexOf(stringUnit.encryptContent); - if (index < 0) { - return literal; - } - - return factory.createCallExpression( - factory.createIdentifier(this.indexFuncName), - undefined, - [factory.createNumericLiteral(index)] - ); - } - - public prepareArrayFunctionStruct(): FunctionDeclaration { - let statements: Statement[] = []; - - // string nodes in array - let arrNodes: Expression[] = []; - this.stringArray.forEach((element) => { - arrNodes.push(factory.createStringLiteral(element)); - }); - - let arrName: string = this.mNameGenerator.getName(); - let arrStruct: VariableDeclaration = factory.createVariableDeclaration( - factory.createIdentifier(arrName), - undefined, - undefined, - factory.createArrayLiteralExpression( - arrNodes, - true - ) - ); - - let arrFuncStruct: VariableDeclaration = factory.createVariableDeclaration( - factory.createIdentifier(this.arrayFuncName), - undefined, - undefined, - factory.createFunctionExpression( - undefined, - undefined, - undefined, - undefined, - [], - undefined, - factory.createBlock( - [factory.createReturnStatement(factory.createIdentifier(arrName))], - true - ) - ) - ); - - let declarationStatement: VariableStatement = factory.createVariableStatement(undefined, - factory.createVariableDeclarationList([arrStruct, arrFuncStruct], NodeFlags.Const)); - statements.push(declarationStatement); - - let callExpr: CallExpression = factory.createCallExpression( - factory.createIdentifier(this.arrayFuncName), - undefined, - [] - ); - - let returnStatement: ReturnStatement = factory.createReturnStatement(callExpr); - statements.push(returnStatement); - - return factory.createFunctionDeclaration( - undefined, - undefined, - undefined, - factory.createIdentifier(this.arrayFuncName), - undefined, - [], - undefined, - factory.createBlock(statements, true) - ); - } - - public prepareIndexFunctionStruct(): FunctionDeclaration { - let statements: Statement[] = []; - let indexName: string = this.mNameGenerator.getName(); - let arrName: string = this.mNameGenerator.getName(); - - let arrStruct: VariableDeclaration = factory.createVariableDeclaration( - factory.createIdentifier(arrName), - undefined, - undefined, - factory.createCallExpression( - factory.createIdentifier(this.arrayFuncName), - undefined, - [] - )); - - let indexFuncStruct: VariableDeclaration = factory.createVariableDeclaration( - factory.createIdentifier(this.indexFuncName), - undefined, - undefined, - factory.createFunctionExpression( - undefined, - undefined, - undefined, - undefined, - [factory.createParameterDeclaration( - undefined, - undefined, - undefined, - factory.createIdentifier(indexName), - undefined, - undefined, - undefined, - )], - undefined, - factory.createBlock( - [ - factory.createReturnStatement(factory.createElementAccessExpression( - factory.createIdentifier(arrName), - factory.createIdentifier(indexName), - )) - ] - ) - ) - ); - - let declaration: VariableStatement = factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - arrStruct, - indexFuncStruct, - ], - NodeFlags.Const - )); - statements.push(declaration); - - let callExpr: CallExpression = factory.createCallExpression( - factory.createIdentifier(this.indexFuncName), - undefined, - [factory.createIdentifier(indexName)]); - - if (this.profile.mEncryptType === EncryptType.BASE64) { - let decryptName: string = this.mNameGenerator.getName(); - let decryptStruct: Statement = this.prepareDecryptFuncStruct(decryptName); - statements.push(decryptStruct); - callExpr = factory.createCallExpression(factory.createIdentifier(decryptName), undefined, [callExpr]); - } - - let returnStatement: ReturnStatement = factory.createReturnStatement(callExpr); - statements.push(returnStatement); - - let paramStruct: ParameterDeclaration = factory.createParameterDeclaration( - undefined, - undefined, - undefined, - factory.createIdentifier(indexName), - undefined, - undefined, - undefined - ); - - return factory.createFunctionDeclaration( - undefined, - undefined, - undefined, - factory.createIdentifier(this.indexFuncName), - undefined, - [paramStruct], - undefined, - factory.createBlock(statements, true)); - } - - public isTargetStr(str: string): boolean { - return this.stringUnits.has(str); - } - - public prepareDecryptFuncStruct(name: string): Statement { - let names: string[] = []; - - names.push(name); - names.push(this.mNameGenerator.getName()); - names.push(this.mNameGenerator.getName()); - names.push(this.mNameGenerator.getName()); - names.push(this.mNameGenerator.getName()); - names.push(this.mNameGenerator.getName()); - names.push(this.mNameGenerator.getName()); - names.push(this.mNameGenerator.getName()); - names.push(this.mNameGenerator.getName()); - - return new Base64Helper().decodeStruct(names); - } -} diff --git a/arkguard/src/transformers/data/StringUnit.ts b/arkguard/src/transformers/data/StringUnit.ts deleted file mode 100644 index 45999ae1cdfd7d5d1ee7a2552774797ce99dbd6f..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/data/StringUnit.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type {Node, StringLiteralLike} from 'typescript'; - -export enum EncryptType { - NONE = 0, - BASE64 = 1, - RC4 = 2, - AES256 = 3, -} - -export interface StringUnit { - // content of a string unit - 'content': string, - // order of string in array - 'order': number; - // related node list of current string unit - 'nodeList': Node[]; - // encrypt algorithm for string - 'encryptAlgo': EncryptType; - // content after encryption. - 'encryptContent': string; -} - -export function createStringUnit(node: StringLiteralLike, index: number = -1): StringUnit { - return { - content: node.text, - order: index, - nodeList: [node], - encryptAlgo: EncryptType.NONE, - encryptContent: node.text, - }; -} diff --git a/arkguard/src/transformers/instruction/InstructionObfsHelper.ts b/arkguard/src/transformers/instruction/InstructionObfsHelper.ts deleted file mode 100644 index 8a01e81b002e1014b67e87e71d21bf369f022c58..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/instruction/InstructionObfsHelper.ts +++ /dev/null @@ -1,678 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - factory, - NodeFlags, - SyntaxKind, -} from 'typescript'; - -import type { - BinaryExpression, - Block, - CallExpression, - Expression, - FunctionExpression, - Identifier, - ObjectLiteralExpression, - ParameterDeclaration, - ParenthesizedExpression, - PropertyAssignment, - VariableStatement -} from 'typescript'; - -import * as crypto from 'crypto'; -import type {INameGenerator} from '../../generator/INameGenerator'; -import {NodeUtils} from '../../utils/NodeUtils'; - -export class InstructionHelper { - private readonly mArgcFuncMap: Map; - private readonly mReservedIdentifiers: Set; - private readonly mNameGenerator: INameGenerator; - - constructor(nameGenerator: INameGenerator) { - this.mArgcFuncMap = new Map(); - this.mReservedIdentifiers = new Set(); - this.mNameGenerator = nameGenerator; - } - - /** - * ignore deform for special function call - * @param callExpression - * @private - */ - private isSpecialFunctionCall(callExpression: CallExpression): boolean { - return callExpression.expression.kind === SyntaxKind.SuperKeyword; - } - - /** - * deform call expression, only support function of identifier expression - * @param callExpression - * @param varName - * @param pairArray - */ - public deformCallExpression(callExpression: CallExpression, varName: string, pairArray: PropertyAssignment[]): CallExpression { - if (this.isSpecialFunctionCall(callExpression)) { - return callExpression; - } - - // parse the information in the original instruction: function name, parameter list, and number of parameters - const argsCount: number = callExpression.arguments.length; - - for (const arg of callExpression.arguments) { - if (arg.kind === SyntaxKind.Identifier) { - this.mReservedIdentifiers.add((arg as Identifier).text); - } - } - - const funcName: Expression = {...callExpression.expression}; - - // if there is no deformation function corresponding to the number of parameters, - // generate a new pair, add it to the pairArray, and update the argcMap - if (this.mArgcFuncMap.get(argsCount) === undefined) { - const newPair: PropertyAssignment = this.createPair(argsCount); - pairArray.push(newPair); - } - - // query the corresponding deformation function name in argcMap and deform callExpression - return factory.createCallExpression( - factory.createElementAccessExpression( - factory.createIdentifier(varName), - factory.createStringLiteral(this.mArgcFuncMap.get(argsCount)) - ), - undefined, - [ - funcName, - ...callExpression.arguments - ] - ); - } - - public getReservedIdentifiers(): Set { - return this.mReservedIdentifiers; - } - - public createPair(argsCount: number): PropertyAssignment { - const key: string = this.mNameGenerator.getName(); - this.mArgcFuncMap.set(argsCount, key); - - return factory.createPropertyAssignment( - factory.createStringLiteral(key), - this.createFunc(argsCount) - ); - } - - private createFunc(argsCount: number): FunctionExpression { - let parametersList: ParameterDeclaration[] = []; - let parameterNameList: string[] = []; - - // 1. create function parameter - for (let i = 0; i < argsCount + 1; i++) { - const parameterName: string = this.mNameGenerator.getName(); - parameterNameList.push(parameterName); - - const funcParameter: ParameterDeclaration = factory.createParameterDeclaration( - undefined, - undefined, - undefined, - factory.createIdentifier(parameterName) - ); - parametersList.push(funcParameter); - } - - // 2. create call expression parameter - const callParametersList: Identifier[] = []; - for (let i = 1; i < argsCount + 1; i++) { - callParametersList.push(factory.createIdentifier(parameterNameList[i])); - } - - // 3. create function body - const funcBlock: Block = factory.createBlock( - [ - factory.createReturnStatement( - factory.createCallExpression( - factory.createIdentifier(parameterNameList[0]), - undefined, - callParametersList - ) - ) - ], - true - ); - - return factory.createFunctionExpression( - undefined, - undefined, - undefined, - undefined, - parametersList, - undefined, - funcBlock - ); - } - - public createCallMapStatement(varName: string, pairArray: PropertyAssignment[]): VariableStatement { - const value: ObjectLiteralExpression = this.createCallMapValue(pairArray); - - return factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier(varName), - undefined, - undefined, - value - ) - ], - NodeFlags.Const - ) - ); - } - - private createCallMapValue(pairArray: PropertyAssignment[]): ObjectLiteralExpression { - return factory.createObjectLiteralExpression( - pairArray, - true - ); - } - - public obfuscateBinaryExpression(binaryExpression: BinaryExpression): Expression { - switch (binaryExpression.operatorToken.kind) { - case SyntaxKind.MinusToken: - return this.createMinusMBA(binaryExpression); - case SyntaxKind.CaretToken: - // a^b - return this.createCaretMBA(binaryExpression); - case SyntaxKind.BarToken: - // a|b - return this.createBarMBA(binaryExpression); - case SyntaxKind.AmpersandToken: - // a&b - return this.createAmpersandMBA(binaryExpression); - default: - return binaryExpression; - } - } - - private createMinusMBA(minusExpression: BinaryExpression): BinaryExpression { - /** - * decimal part: x - y + Math.truc(y) - Math.trunc(x) - */ - function getDecimalMinus(): BinaryExpression { - return factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createBinaryExpression( - {...minusExpression.left}, - SyntaxKind.MinusToken, - {...minusExpression.right} - ), - SyntaxKind.PlusToken, - NodeUtils.createTruncExpression(minusExpression.right) - ), - SyntaxKind.MinusToken, - NodeUtils.createTruncExpression(minusExpression.left) - ); - } - - /** - * get minus expression of higher 32 bit - * trunc(x) - (x|0) - trunc(y) + (y|0) - */ - function getHigh32Minus(): BinaryExpression { - return factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createBinaryExpression( - NodeUtils.createTruncExpression(minusExpression.left), - SyntaxKind.MinusToken, - NodeUtils.createLowerExpression(minusExpression.left) - ), - SyntaxKind.MinusToken, - NodeUtils.createTruncExpression(minusExpression.right) - ), - SyntaxKind.PlusToken, - NodeUtils.createLowerExpression(minusExpression.right) - ); - } - - /** - * x - y = (x|0) + ((y^-1) + 1), for lower 32 bit - */ - function method1(): BinaryExpression { - const right: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...minusExpression.right}, - SyntaxKind.CaretToken, - factory.createPrefixUnaryExpression( - SyntaxKind.MinusToken, - factory.createNumericLiteral('1') - ) - ) - ), - SyntaxKind.PlusToken, - factory.createNumericLiteral('1') - ) - ); - - return factory.createBinaryExpression( - NodeUtils.createLowerExpression(minusExpression.left), - SyntaxKind.PlusToken, - right - ); - } - - /** - * x - y = (x ^ (~y+1)) - ((-2*x - 1) | (2*y - 1)) - 1 - */ - function method2(): BinaryExpression { - const first: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...minusExpression.left}, - SyntaxKind.CaretToken, - factory.createParenthesizedExpression( - factory.createBinaryExpression( - factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - {...minusExpression.right} - ), - SyntaxKind.PlusToken, - factory.createNumericLiteral('1') - ) - ) - ) - ); - - const secondLeft: BinaryExpression = factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createPrefixUnaryExpression( - SyntaxKind.MinusToken, - factory.createNumericLiteral('2') - ), - SyntaxKind.AsteriskToken, - NodeUtils.createLowerExpression(minusExpression.left) - ), - SyntaxKind.MinusToken, - factory.createNumericLiteral('1') - ); - - const secondRight: BinaryExpression = factory.createBinaryExpression( - factory.createBinaryExpression( - factory.createNumericLiteral('2'), - SyntaxKind.AsteriskToken, - NodeUtils.createLowerExpression(minusExpression.right) - ), - SyntaxKind.MinusToken, - factory.createNumericLiteral('1') - ); - - const second: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - factory.createParenthesizedExpression(secondLeft), - SyntaxKind.BarToken, - factory.createParenthesizedExpression(secondRight) - ) - ); - - return factory.createBinaryExpression( - factory.createBinaryExpression( - first, - SyntaxKind.MinusToken, - second - ), - SyntaxKind.MinusToken, - factory.createNumericLiteral('1') - ); - } - - /** - * x - y = (x & ~y) - (~x & y) - */ - function method3(): BinaryExpression { - const left: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...minusExpression.left}, - SyntaxKind.AmpersandToken, - factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - {...minusExpression.right} - ) - ) - ); - - const right: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - {...minusExpression.left} - ), - SyntaxKind.AmpersandToken, - {...minusExpression.right} - ) - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.MinusToken, - right - ); - } - - /** - * x - y = ~(~x + y) - */ - function method4(): Expression { - const inner: BinaryExpression = factory.createBinaryExpression( - factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - {...minusExpression.left} - ), - SyntaxKind.PlusToken, - NodeUtils.createLowerExpression(minusExpression.right) - ); - - return factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - factory.createParenthesizedExpression(inner) - ); - } - - const methodList: (() => Expression)[] = [method1, method2, method3, method4]; - const mbaMethod: () => Expression = methodList[crypto.randomInt(methodList.length)]; - - const decimalPart: BinaryExpression = getDecimalMinus(); - const highPart: BinaryExpression = getHigh32Minus(); - - return factory.createBinaryExpression( - factory.createBinaryExpression( - mbaMethod(), - SyntaxKind.PlusToken, - highPart - ), - SyntaxKind.PlusToken, - decimalPart - ); - } - - private createCaretMBA(xorExpression: BinaryExpression): BinaryExpression { - /** - * x ^ y = (x | y) - (x & y) - */ - function method1(): BinaryExpression { - const left: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...xorExpression.left}, - SyntaxKind.BarToken, - {...xorExpression.right} - ) - ); - - const right: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...xorExpression.left}, - SyntaxKind.AmpersandToken, - {...xorExpression.right} - ) - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.MinusToken, - right - ); - } - - /** - * x ^ y = x + y - 2*(x & y) - */ - function method2(): BinaryExpression { - const left: BinaryExpression = factory.createBinaryExpression( - NodeUtils.createLowerExpression(xorExpression.left), - SyntaxKind.PlusToken, - NodeUtils.createLowerExpression(xorExpression.right) - ); - - const right: BinaryExpression = factory.createBinaryExpression( - factory.createNumericLiteral('2'), - SyntaxKind.AsteriskToken, - factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...xorExpression.left}, - SyntaxKind.AmpersandToken, - {...xorExpression.right} - ) - ) - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.MinusToken, - right - ); - } - - const methodList: (() => BinaryExpression)[] = [method1, method2]; - const mbaMethod: () => BinaryExpression = methodList[crypto.randomInt(methodList.length)]; - return mbaMethod(); - } - - private createBarMBA(orExpression: BinaryExpression): BinaryExpression { - /** - * x | y = (x ^ y) ^ (x & y) - */ - function method1(): BinaryExpression { - const left: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...orExpression.left}, - SyntaxKind.CaretToken, - {...orExpression.right} - ) - ); - - const right: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...orExpression.left}, - SyntaxKind.AmpersandToken, - {...orExpression.right} - ) - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.CaretToken, - right - ); - } - - /** - * x | y = x + y - (x & y) - */ - function method2(): BinaryExpression { - const left: BinaryExpression = factory.createBinaryExpression( - NodeUtils.createLowerExpression(orExpression.left), - SyntaxKind.PlusToken, - NodeUtils.createLowerExpression(orExpression.right) - ); - - const right: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...orExpression.left}, - SyntaxKind.AmpersandToken, - {...orExpression.right} - ) - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.MinusToken, - right - ); - } - - /** - * x | y = (x & y) | (x ^ y) - */ - function method3(): BinaryExpression { - const left: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...orExpression.left}, - SyntaxKind.AmpersandToken, - {...orExpression.right} - ) - ); - - const right: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...orExpression.left}, - SyntaxKind.CaretToken, - {...orExpression.right} - ) - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.BarToken, - right - ); - } - - const methodList: (() => BinaryExpression)[] = [method1, method2, method3]; - const mbaMethod: () => BinaryExpression = methodList[crypto.randomInt(methodList.length)]; - return mbaMethod(); - } - - private createAmpersandMBA(andExpression: BinaryExpression): Expression { - /** - * x & y = ~(~x | ~y) - */ - function method1(): Expression { - const inner: BinaryExpression = factory.createBinaryExpression( - factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - {...andExpression.left} - ), - SyntaxKind.BarToken, - factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - {...andExpression.right} - ) - ); - - return factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - factory.createParenthesizedExpression( - inner - ) - ); - } - - /** - * x & y = x + y - (x | y) - */ - function method2(): BinaryExpression { - const left: BinaryExpression = factory.createBinaryExpression( - NodeUtils.createLowerExpression(andExpression.left), - SyntaxKind.PlusToken, - NodeUtils.createLowerExpression(andExpression.right) - ); - - const right: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...andExpression.left}, - SyntaxKind.BarToken, - {...andExpression.right} - ) - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.MinusToken, - right - ); - } - - /** - * x & y = (x | y) - (~x & y) - (x & ~y) - */ - function method3(): BinaryExpression { - const first: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...andExpression.left}, - SyntaxKind.BarToken, - {...andExpression.right} - ) - ); - - const second: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - {...andExpression.left} - ), - SyntaxKind.AmpersandToken, - {...andExpression.right} - ) - ); - - const third: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...andExpression.left}, - SyntaxKind.AmpersandToken, - factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - {...andExpression.right} - ) - ) - ); - - return factory.createBinaryExpression( - factory.createBinaryExpression( - first, - SyntaxKind.MinusToken, - second - ), - SyntaxKind.MinusToken, - third - ); - } - - /** - * x & y = (x ^ ~y) & x - */ - function method4(): BinaryExpression { - const left: ParenthesizedExpression = factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...andExpression.left}, - SyntaxKind.CaretToken, - factory.createPrefixUnaryExpression( - SyntaxKind.TildeToken, - {...andExpression.right} - ) - ) - ); - - return factory.createBinaryExpression( - left, - SyntaxKind.AmpersandToken, - {...andExpression.left} - ); - } - - const methodList: (() => Expression)[] = [method1, method2, method3, method4]; - const mbaMethod: () => Expression = methodList[crypto.randomInt(methodList.length)]; - return mbaMethod(); - } -} diff --git a/arkguard/src/transformers/instruction/InstructionObfuscationTransformer.ts b/arkguard/src/transformers/instruction/InstructionObfuscationTransformer.ts deleted file mode 100644 index e2ed2843e28235324b84bbd6ae7b635ff06d82a3..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/instruction/InstructionObfuscationTransformer.ts +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as crypto from 'crypto'; - -import { - factory, - isBinaryExpression, - isCallExpression, - isDecorator, - isElementAccessExpression, - isIdentifier, - isPropertyAccessExpression, - isSourceFile, - NodeFlags, - setParentRecursive, - SyntaxKind, - visitEachChild -} from 'typescript'; - -import type { - BinaryExpression, - BinaryOperator, - Block, - CallExpression, - CaseOrDefaultClause, - Expression, - FunctionDeclaration, - Identifier, - Node, - ParameterDeclaration, - PropertyAccessExpression, - PropertyAssignment, SourceFile, - Statement, SwitchStatement, - TransformationContext, - Transformer, - TransformerFactory, - VariableStatement -} from 'typescript'; - -import type {TransformPlugin} from '../TransformPlugin'; -import type {IOptions} from '../../configs/IOptions'; -import {InstructionObfsMethod} from '../../configs/IInstructionObfuscationOption'; -import type {IInstructionObfuscationOption} from '../../configs/IInstructionObfuscationOption'; -import {InstructionHelper} from './InstructionObfsHelper'; -import {NodeUtils} from '../../utils/NodeUtils'; -import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; -import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; -import {collectExistNames, isObfsIgnoreNode} from '../../utils/TransformUtil'; - -namespace secharmony { - const createInstructionObfuscationFactory = function (option: IOptions): TransformerFactory { - let profile: IInstructionObfuscationOption | undefined = option?.mInstructionObfuscation; - if (!profile || !profile.mEnable || profile.mThreshold <= 0) { - return null; - } - - return instructionObfuscationFactory; - - function instructionObfuscationFactory(context: TransformationContext): Transformer { - const skipLoop: boolean = profile.mSkipLoop; - let instructionHelper: InstructionHelper; - let narrowNames: string[] = option?.mNarrowFunctionNames ?? []; - // for call expression - let varName: string; - let pairArray: PropertyAssignment[] = []; - let reservedNames: Set; - let reservedIdentifiers: Set; - let sourceFile: SourceFile; - - // for binary expression - let replaceMethod: InstructionObfsMethod = profile.mInstructionObfsMethod; - const methodTypeMap: Map = new Map(); - let deformFuncName: string; - const seed: string = '0x' + crypto.randomBytes(1).toString('hex'); - let simpleDeformed: boolean = false; - - return transformer; - - function transformer(node: Node): Node { - if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { - return node; - } - - sourceFile = node; - reservedIdentifiers = collectExistNames(node); - - const options: NameGeneratorOptions = { - reservedNames: reservedIdentifiers - }; - const nameGenerator: INameGenerator = getNameGenerator(NameGeneratorType.ORDERED, options); - instructionHelper = new InstructionHelper(nameGenerator); - varName = nameGenerator.getName(); - deformFuncName = nameGenerator.getName(); - const functionDeclare: FunctionDeclaration = createSimpleDeformFunction(deformFuncName, seed); - - let obfuscatedAst: Node = visitAst(node); - - reservedNames = instructionHelper.getReservedIdentifiers(); - if (reservedNames.size > 0) { - obfuscatedAst = changeReservedNamesAccess(obfuscatedAst); - } - - if (!isSourceFile(obfuscatedAst)) { - return node; - } - - let newStatements: Statement[] = [...obfuscatedAst.statements]; - if (simpleDeformed) { - newStatements = NodeUtils.randomInsertStatements(newStatements, functionDeclare); - } - - if (pairArray.length > 0) { - const initStatement: VariableStatement = instructionHelper.createCallMapStatement(varName, pairArray); - newStatements = [initStatement, ...newStatements]; - } - - // must use update, don't create here, otherwise will encounter an issue for printer. - const newAst: SourceFile = factory.updateSourceFile(node, newStatements); - return setParentRecursive(newAst, true); - } - - function visitAst(node: Node): Node { - if (isDecorator(node)) { - return node; - } - - if (skipLoop && NodeUtils.isLoopStatement(node)) { - return node; - } - - if (!isSourceFile(node) && isObfsIgnoreNode(node, sourceFile)) { - return node; - } - - // only replace most inner instruction - if ((!isCallExpression(node) || !NodeUtils.isMostInnerCallExpression(node)) && - (!isBinaryExpression(node) || !NodeUtils.isMostInnerBinary(node))) { - return visitEachChild(node, visitAst, context); - } - - const newNode: BinaryExpression | CallExpression = visitEachChild(node, visitAst, context); - return replaceInstruction(newNode); - } - - /** - * change property access to element access of reserved names - * @param node - */ - function changeReservedNamesAccess(node: Node): Node { - if (!isPropertyAccessExpression(node)) { - return visitEachChild(node, changeReservedNamesAccess, context); - } - - if (!isIdentifier(node.expression)) { - return visitEachChild(node, changeReservedNamesAccess, context); - } - - if (!reservedNames.has(node.expression.escapedText.toString())) { - return node; - } - - const newNode: PropertyAccessExpression = visitEachChild(node, changeReservedNamesAccess, context); - return NodeUtils.changePropertyAccessToElementAccess(newNode); - } - - function simpleDeformBinary(binaryExpression: BinaryExpression): Expression { - if (methodTypeMap.get(binaryExpression.operatorToken.kind) === undefined) { - return binaryExpression; - } - - if (binaryExpression.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || - binaryExpression.operatorToken.kind === SyntaxKind.BarBarToken) { - return binaryExpression; - } - - const type: string = methodTypeMap.get(binaryExpression.operatorToken.kind); - const HEX_RADIX: number = 16; - const fakeValue: number = parseInt(type, HEX_RADIX) - parseInt(seed, HEX_RADIX); - let fakeValueHexStr: string = '0x' + (Math.abs(fakeValue)).toString(HEX_RADIX); - if (fakeValue < 0) { - fakeValueHexStr = '-' + fakeValueHexStr; - } - - simpleDeformed = true; - return factory.createCallExpression( - factory.createIdentifier(deformFuncName), - undefined, - [ - {...binaryExpression.left}, - {...binaryExpression.right}, - factory.createNumericLiteral(fakeValueHexStr) - ] - ); - } - - function replaceInstruction(node: Node): Node { - // judge threshold - const RANDOM_MAX: number = 100; - const temp: number = crypto.randomInt(RANDOM_MAX); - if (temp > RANDOM_MAX * profile.mThreshold) { - return node; - } - - if (isCallExpression(node)) { - if (isPropertyAccessExpression(node.expression) || - isElementAccessExpression(node.expression) || - !isIdentifier(node.expression)) { - return node; - } - - if (narrowNames.includes((node.expression as Identifier).text)) { - return node; - } - - return instructionHelper.deformCallExpression(node, varName, pairArray); - } - - if (isBinaryExpression(node)) { - if (replaceMethod !== InstructionObfsMethod.MBA_EXPRESSION) { - return simpleDeformBinary(node); - } - - const replacedBinary: Expression = instructionHelper.obfuscateBinaryExpression(node); - if (replacedBinary !== node) { - return replacedBinary; - } - - return simpleDeformBinary(node); - } - - return node; - } - - function generateMethodTypeMap(): void { - const methodList: SyntaxKind[] = [ - SyntaxKind.PlusToken, SyntaxKind.MinusToken, SyntaxKind.AsteriskToken, - SyntaxKind.SlashToken, SyntaxKind.AmpersandToken, SyntaxKind.BarToken, - SyntaxKind.CaretToken, SyntaxKind.BarBarToken, SyntaxKind.AmpersandAmpersandToken, - SyntaxKind.EqualsEqualsToken, SyntaxKind.ExclamationEqualsToken, - SyntaxKind.EqualsEqualsEqualsToken, SyntaxKind.ExclamationEqualsEqualsToken, - SyntaxKind.LessThanToken, SyntaxKind.LessThanEqualsToken, - SyntaxKind.GreaterThanToken, SyntaxKind.GreaterThanEqualsToken, - SyntaxKind.LessThanLessThanToken, SyntaxKind.GreaterThanGreaterThanToken, - SyntaxKind.GreaterThanGreaterThanGreaterThanToken, - SyntaxKind.PercentToken, SyntaxKind.InstanceOfKeyword, - SyntaxKind.InKeyword - ]; - - const options: NameGeneratorOptions = { - hexLength: 2, - hexWithPrefixSuffix: false - }; - - const typeGenerator: INameGenerator = getNameGenerator(NameGeneratorType.HEX, options); - methodList.forEach((method) => { - methodTypeMap.set(method, '0x' + typeGenerator.getName()); - }); - } - - /** - * create simple deform function of calculate binary expression - * function parameter use common name because name obfuscate will be done after this transformer - * index transform prototype: x+y = (x|y) + (x&y) - * @param functionName - * @param seed - * @private - */ - function createSimpleDeformFunction(functionName: string, seed: string): FunctionDeclaration { - const parameters: ParameterDeclaration[] = [ - factory.createParameterDeclaration( - undefined, - undefined, - undefined, - factory.createIdentifier('left') - ), - factory.createParameterDeclaration( - undefined, - undefined, - undefined, - factory.createIdentifier('right') - ), - factory.createParameterDeclaration( - undefined, - undefined, - undefined, - factory.createIdentifier('type') - ) - ]; - - const valueDeclare: VariableStatement = factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier('value'), - undefined, - undefined, - factory.createBinaryExpression( - factory.createParenthesizedExpression( - factory.createBinaryExpression( - factory.createNumericLiteral(seed), - SyntaxKind.BarToken, - factory.createIdentifier('type') - ) - ), - SyntaxKind.PlusToken, - factory.createParenthesizedExpression( - factory.createBinaryExpression( - factory.createNumericLiteral(seed), - SyntaxKind.AmpersandToken, - factory.createIdentifier('type') - ) - ) - ) - ) - ], - NodeFlags.Const - ) - ); - - const caseClauses: CaseOrDefaultClause[] = []; - generateMethodTypeMap(); - for (const method of methodTypeMap.keys()) { - caseClauses.push( - factory.createCaseClause( - factory.createNumericLiteral(methodTypeMap.get(method)), - [ - factory.createReturnStatement( - factory.createBinaryExpression( - factory.createIdentifier('left'), - method as BinaryOperator, - factory.createIdentifier('right') - ) - ) - ] - ) - ); - } - - caseClauses.push( - factory.createDefaultClause( - [ - factory.createReturnStatement( - factory.createNumericLiteral(seed) - ) - ] - ) - ); - - const switchStatement: SwitchStatement = factory.createSwitchStatement( - factory.createIdentifier('value'), - factory.createCaseBlock(caseClauses) - ); - - const body: Block = factory.createBlock( - [ - valueDeclare, - switchStatement - ], - true - ); - - return factory.createFunctionDeclaration( - undefined, - undefined, - undefined, - factory.createIdentifier(functionName), - undefined, - parameters, - undefined, - body - ); - } - } - }; - - const TRANSFORMER_ORDER: number = 5; - export let transformerPlugin: TransformPlugin = { - 'name': 'InstructionObfuscationTransformer', - 'createTransformerFactory': createInstructionObfuscationFactory, - 'order': (1 << TRANSFORMER_ORDER) - }; -} - -export = secharmony; diff --git a/arkguard/src/transformers/layout/SimplifyTransformer.ts b/arkguard/src/transformers/layout/SimplifyTransformer.ts index fbea88e0968784abf0da44165c9b0967e93dc173..b40c612eab7b329b6eb19bfafd5df6c6ec4a1ca0 100644 --- a/arkguard/src/transformers/layout/SimplifyTransformer.ts +++ b/arkguard/src/transformers/layout/SimplifyTransformer.ts @@ -47,7 +47,7 @@ import type {TransformPlugin} from '../TransformPlugin'; import {isCommentedNode, isSuperCallStatement} from '../../utils/TransformUtil'; namespace secharmony { - const TRANSFORMER_ORDER: number = 10; + const TRANSFORMER_ORDER: number = 5; export let transformerPlugin: TransformPlugin = { 'name': 'simplifyPlugin', 'order': (1 << TRANSFORMER_ORDER), diff --git a/arkguard/src/transformers/oh/HideOhApiTransformer.ts b/arkguard/src/transformers/oh/HideOhApiTransformer.ts deleted file mode 100644 index 50ae52abd866b51987f34d159a49d680ca38c3c1..0000000000000000000000000000000000000000 --- a/arkguard/src/transformers/oh/HideOhApiTransformer.ts +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - factory, - isBlock, - isElementAccessExpression, - isPropertyAccessExpression, - isSourceFile, - isStringLiteral, - setParentRecursive, - visitEachChild -} from 'typescript'; - -import type { - Block, - CallExpression, - Node, - NodeArray, - SourceFile, - Statement, - TransformationContext, - Transformer, - TransformerFactory -} from 'typescript'; - -import type {IOptions} from '../../configs/IOptions'; -import type {TransformPlugin} from '../TransformPlugin'; -import {collectExistNames, OhPackType} from '../../utils/TransformUtil'; -import {findOhImportStatement} from '../../utils/OhsUtil'; -import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGenerator'; -import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; - -namespace secharmony { - const TRANSFORMER_ORDER: number = 3; - export let transformerPlugin: TransformPlugin = { - 'name': 'hideOhApiPlugin', - 'order': (1 << TRANSFORMER_ORDER), - 'createTransformerFactory': createHideOhApiFactory - }; - - interface OhHiddenApiInfo { - moduleNames: Set, - apis: Set - } - - export function createHideOhApiFactory(option: IOptions): TransformerFactory { - if (!option.mHideOhApi || !option.mHideOhApi.mEnable) { - return null; - } - - if (!option.mHideOhApi.mProtectedApi || option.mHideOhApi.mProtectedApi.length < 1) { - return null; - } - - return hideOhApiFactory; - - function hideOhApiFactory(context: TransformationContext): Transformer { - let nameGenerator: INameGenerator; - let ohHiddenApiInfo: OhHiddenApiInfo; - - return transformer; - - function transformer(node: Node): Node { - if (!isSourceFile(node) || node.fileName.endsWith('.d.ts')) { - return node; - } - - const reservedIdentifiers: Set = collectExistNames(node); - const options: NameGeneratorOptions = { - reservedNames: reservedIdentifiers - }; - nameGenerator = getNameGenerator(NameGeneratorType.ORDERED, options); - - ohHiddenApiInfo = processOhApi(option.mHideOhApi.mProtectedApi); - - let resultAst: Node = visitAst(node); - return setParentRecursive(resultAst, true); - } - - function visitAst(node: Node): Node { - if (isSourceFile(node)) { - const hiddenNode: SourceFile = visitEachChild(node, visitAst, context); - const newStatements: Statement[] = hideStatements(hiddenNode.statements, nameGenerator, ohHiddenApiInfo, context); - return factory.updateSourceFile(hiddenNode, newStatements); - } - - if (isBlock(node)) { - const hiddenNode: Block = visitEachChild(node, visitAst, context); - const newStatements: Statement[] = hideStatements(hiddenNode.statements, nameGenerator, ohHiddenApiInfo, context); - if (newStatements === undefined) { - return hiddenNode; - } - - return factory.createBlock(newStatements, true); - } - - return visitEachChild(node, visitAst, context); - } - } - } - - function hideStatements(statements: NodeArray, nameGenerator: INameGenerator, ohHiddenApiInfo: OhHiddenApiInfo, - context: TransformationContext): Statement[] { - let newStatements: Statement[] = [...statements]; - const apiHiddenMap: Map = new Map(); - - for (let i = 0; i < statements.length; i++) { - // 1. hide api import - for (const module of ohHiddenApiInfo.moduleNames) { - const ohPackType: OhPackType = findOhImportStatement(statements[i], module); - if (ohPackType === OhPackType.NONE) { - continue; - } - - const moduleStr: string = ohPackType === OhPackType.JS_BUNDLE ? module : module.substring('@ohos.'.length); - const hiddenFuncName: string = nameGenerator.getName(); - const hiddenStatement: Statement = createHiddenStatement(hiddenFuncName, moduleStr); - - newStatements[i] = hideOhStr(statements[i], hiddenFuncName, moduleStr, context); - newStatements.push(hiddenStatement); - break; - } - - // 2. hide api - newStatements[i] = hideOhApi(newStatements[i], ohHiddenApiInfo.apis, nameGenerator, context, apiHiddenMap); - } - - for (const key of apiHiddenMap.keys()) { - newStatements.push(createHiddenStatement(apiHiddenMap.get(key), key)); - } - - return newStatements; - } - - function hideOhStr(node: Statement, hiddenFuncName: string, hiddenStr: string, context: TransformationContext): Statement { - let visit = (node: Node): Node => { - if (isStringLiteral(node) && node.text === hiddenStr) { - return factory.createCallExpression(factory.createIdentifier(hiddenFuncName), undefined, []); - } - - return visitEachChild(node, visit, context); - }; - - return visit(node) as Statement; - } - - function hideOhApi(node: Statement, apiNames: Set, nameGenerator: INameGenerator, context: TransformationContext, apiHiddenMap): Statement { - let visit = (node: Node): Node => { - if (isBlock(node)) { - return node; - } - - if (isPropertyAccessExpression(node)) { - if (!apiNames.has(node.name.text)) { - return node; - } - - const hiddenFuncName: string = apiHiddenMap.has(node.name.text) ? - apiHiddenMap.get(node.name.text) : nameGenerator.getName(); - if (!apiHiddenMap.has(node.name.text)) { - apiHiddenMap.set(node.name.text, hiddenFuncName); - } - - const hiddenCall: CallExpression = factory.createCallExpression( - factory.createIdentifier(hiddenFuncName), - undefined, - [] - ); - return factory.createElementAccessExpression(node.expression, hiddenCall); - } - - if (isElementAccessExpression(node)) { - if (!isStringLiteral(node.argumentExpression)) { - return node; - } - - if (!apiNames.has(node.argumentExpression.text)) { - return node; - } - - const hiddenFuncName: string = apiHiddenMap.has(node.argumentExpression.text) ? - apiHiddenMap.get(node.argumentExpression.text) : nameGenerator.getName(); - if (!apiHiddenMap.has(node.argumentExpression.text)) { - apiHiddenMap.set(node.argumentExpression.text, hiddenFuncName); - } - - const hiddenCall: CallExpression = factory.createCallExpression( - factory.createIdentifier(hiddenFuncName), - undefined, - []); - return factory.createElementAccessExpression(node.expression, hiddenCall); - } - - return visitEachChild(node, visit, context); - }; - - return visit(node) as Statement; - } - - /** - * process api list to get module and api function - * @param apiList - * @private - */ - function processOhApi(apiList: string[]): OhHiddenApiInfo { - let apiInfo: OhHiddenApiInfo = { - moduleNames: new Set(), - apis: new Set() - }; - - for (const api of apiList) { - // check format - const MIN_OFFSET = 2; - if (!api.startsWith('@ohos') || !api.includes('.') || api.lastIndexOf('.') > api.length - MIN_OFFSET) { - continue; - } - - // extract api - apiInfo.moduleNames.add(api.substring(0, api.lastIndexOf('.'))); - apiInfo.apis.add(api.substring(api.lastIndexOf('.') + 1)); - } - - return apiInfo; - } - - function createHiddenStatement(hiddenFuncName: string, hiddenStr: string): Statement { - return factory.createFunctionDeclaration( - undefined, - undefined, - undefined, - factory.createIdentifier(hiddenFuncName), - undefined, - [], - undefined, - factory.createBlock( - [ - factory.createReturnStatement(factory.createStringLiteral(hiddenStr)) - ], false - ) - ); - } -} - -export = secharmony; diff --git a/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts b/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts index a5640380cf9ea3b29e93a11a68049e4104d9e114..aea68051fdcb549218892c14d8ff02540ad7cd09 100644 --- a/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts +++ b/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts @@ -83,13 +83,10 @@ namespace secharmony { return renameIdentifierFactory; function renameIdentifierFactory(context: TransformationContext): Transformer { - let reservedNames: string[] = [...(profile?.mReservedNames ?? []), 'this']; + let reservedNames: string[] = [...(profile?.mReservedNames ?? []), 'this', "__global"]; let mangledSymbolNames: Map = new Map(); let mangledLabelNames: Map = new Map(); - let historyMangledNames: Set = undefined; - if (historyNameCache && historyNameCache.size > 0) { - historyMangledNames = new Set(Array.from(historyNameCache.values())); - } + let options: NameGeneratorOptions = {}; if (profile.mNameGeneratorType === NameGeneratorType.HEX) { options.hexWithPrefixSuffix = true; @@ -97,6 +94,11 @@ namespace secharmony { let generator: INameGenerator = getNameGenerator(profile.mNameGeneratorType, options); + let historyMangledNames: Set = undefined; + if (historyNameCache && historyNameCache.size > 0) { + historyMangledNames = new Set(Array.from(historyNameCache.values())); + } + let checker: TypeChecker = undefined; let manager: ScopeManager = createScopeManager(); let shadowIdentifiers: Identifier[] = undefined; @@ -241,6 +243,11 @@ namespace secharmony { continue; } + if (scope.exportNames && scope.exportNames.has(mangled)) { + mangled = ''; + continue; + } + if (historyMangledNames && historyMangledNames.has(mangled)) { mangled = ''; continue; diff --git a/arkguard/src/transformers/rename/RenamePropertiesTransformer.ts b/arkguard/src/transformers/rename/RenamePropertiesTransformer.ts index 139540432898c04c050f460d509d68c761ab0e05..92675e7081e61abd504286c47fd37cfd8b4be5be 100644 --- a/arkguard/src/transformers/rename/RenamePropertiesTransformer.ts +++ b/arkguard/src/transformers/rename/RenamePropertiesTransformer.ts @@ -16,12 +16,12 @@ import { factory, forEachChild, - isClassDeclaration, isComputedPropertyName, isConstructorDeclaration, isElementAccessExpression, isEnumMember, isIdentifier, + isClassDeclaration, isNumericLiteral, isPrivateIdentifier, isStringLiteralLike, @@ -32,7 +32,6 @@ import { import type { ComputedPropertyName, - EnumMember, Expression, Identifier, Node, @@ -47,7 +46,7 @@ import type {INameGenerator, NameGeneratorOptions} from '../../generator/INameGe import {getNameGenerator, NameGeneratorType} from '../../generator/NameFactory'; import type {TransformPlugin} from '../TransformPlugin'; import {NodeUtils} from '../../utils/NodeUtils'; -import { getClassProperties, isViewPUBasedClass } from '../../utils/OhsUtil'; +import {getClassProperties, isViewPUBasedClass} from '../../utils/OhsUtil'; namespace secharmony { /** @@ -80,8 +79,8 @@ namespace secharmony { let generator: INameGenerator = getNameGenerator(profile.mNameGeneratorType, options); - let reservedProperties: string[] = profile?.mReservedProperties ?? []; - let reservedNamesInEnum: string[] = []; + let tmpReservedProps: string[] = profile?.mReservedProperties ?? []; + let reservedProperties: Set = new Set(tmpReservedProps); let currentConstructorParams: Set = new Set(); @@ -156,7 +155,7 @@ namespace secharmony { } let original: string = node.text; - if (reservedProperties.includes(original)) { + if (reservedProperties.has(original)) { return node; } @@ -179,17 +178,16 @@ namespace secharmony { } function getPropertyName(original: string): string { + if (reservedProperties.has(original)) { + return original; + } + const historyName: string = historyMangledTable?.get(original); let mangledName: string = historyName ? historyName : globalMangledTable.get(original); while (!mangledName) { mangledName = generator.getName(); - if (mangledName === original) { - mangledName = null; - continue; - } - - if (reservedProperties.includes(mangledName)) { + if (mangledName === original || reservedProperties.has(mangledName)) { mangledName = null; continue; } @@ -201,53 +199,46 @@ namespace secharmony { if (reserved.includes(mangledName)) { mangledName = null; - continue; - } - - if (reservedNamesInEnum.includes(mangledName)) { - mangledName = null; } } globalMangledTable.set(original, mangledName); return mangledName; } - // enum syntax has special scenarios - function collectReservedNames(node: Node): void { - if (!isEnumMember(node) && !isClassDeclaration(node)) { - forEachChild(node, collectReservedNames); + function visitEnumInitializer(childNode: Node): void { + if (!isIdentifier(childNode)) { + forEachChild(childNode, visitEnumInitializer); + return; } - // collect viewPU class properties - if (isClassDeclaration(node)) { - if (!isViewPUBasedClass(node)) { - return; - } - const properties = getClassProperties(node); - properties.forEach((property) => { - reservedProperties.push(property); - }); + if (NodeUtils.isPropertyNode(childNode)) { return; } - // collect enum properties - let initial: Expression = (node as EnumMember).initializer; - let visit = function (child: Node): void { - if (!isIdentifier(child)) { - return; - } + if (isTypeNode(childNode)) { + return; + } - if (NodeUtils.isPropertyNode(child)) { - return; - } + reservedProperties.add(childNode.text); + } - if (isTypeNode(child)) { - return; - } - reservedNamesInEnum.push(child.text); - }; + // enum syntax has special scenarios + function collectReservedNames(node: Node): void { + // collect ViewPU class properties + if (isClassDeclaration(node) && isViewPUBasedClass(node)) { + getClassProperties(node, reservedProperties); + return; + } + + // collect reserved name of enum + // example: enum H {A, B = A + 1}, enum H = {A, B= 1 + (A + 1)}; A is reserved + if (isEnumMember(node) && node.initializer) { + // collect enum properties + node.initializer.forEachChild(visitEnumInitializer); + return; + } - forEachChild(initial, visit); + forEachChild(node, collectReservedNames); } } }; diff --git a/arkguard/src/transformers/rename/ShorthandPropertyTransformer.ts b/arkguard/src/transformers/rename/ShorthandPropertyTransformer.ts index 819dd07638d0a9a5c8ab4d01d78add1956adf442..4cd2f3ab06f9e9f29f7cb69dfc194b2a7c983abe 100644 --- a/arkguard/src/transformers/rename/ShorthandPropertyTransformer.ts +++ b/arkguard/src/transformers/rename/ShorthandPropertyTransformer.ts @@ -54,15 +54,21 @@ namespace secharmony { function transformShortHandProperty(node: Node): Node { /** - * 1. let name = 'hello'; let info = {name} + * ShortHandProperty example: + * `let name = 'hello;` + * `let info = {name};` */ if (isShorthandPropertyAssignment((node))) { // update parent return factory.createPropertyAssignment(factory.createIdentifier(node.name.text), node.name); } /** - * const { x, y } = { x: 1, y: 2 }; - * const { x: a, y: b} = { x, y } --> const {x: c, y: d} = { x: e, y: f }; + * orinal ObjectBinding: + * `const { x, y } = { x: 1, y: 2 };` + * `const { x: a, y: b} = { x, y };` + * obfuscated ObjectBinding: + * `const { x: a, y: b } = { x: 1, y: 2 };` + * `const { x: c, y: d } = { x: a, y: b };` */ if (isObjectBindingPattern(node) && NodeUtils.isObjectBindingPatternAssignment(node)) { return node; diff --git a/arkguard/src/utils/EncryptedUtils.ts b/arkguard/src/utils/EncryptedUtils.ts deleted file mode 100644 index f4e9ab9ee031afb753be92fd2f59b896b59c721d..0000000000000000000000000000000000000000 --- a/arkguard/src/utils/EncryptedUtils.ts +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import {createSourceFile, ScriptTarget} from 'typescript'; -import type {Node, SourceFile, Statement} from 'typescript'; -import {NodeUtils} from './NodeUtils'; - -export abstract class BaseEncryptedHelper { - protected constructor() { - } - - public abstract encode(content: string): string; - - public abstract decode(content: string): string; - - public abstract decodeStruct(names: string[]): Node; -} - -export class Base64Helper extends BaseEncryptedHelper { - public constructor() { - super(); - } - - /** - * @param content - */ - public encode(content: string): string { - try { - return Buffer.from(encodeURIComponent(content), 'utf-8').toString('base64'); - } catch (e) { - return null; - } - } - - /** - * @param content - */ - public decode(content: string): string { - let decodedContent: string = decodeURI(content); - let _keyStr: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - let output: string = ''; - let chr1: number; - let chr2: number; - let chr3: number; - let enc1: number; - let enc2: number; - let enc3: number; - let enc4: number; - let i: number = 0; - - decodedContent = decodedContent.replace(/[^A-Za-z0-9\+\/\=]/g, ''); - while (i < decodedContent.length) { - enc1 = _keyStr.indexOf(decodedContent.charAt(i++)); - enc2 = _keyStr.indexOf(decodedContent.charAt(i++)); - enc3 = _keyStr.indexOf(decodedContent.charAt(i++)); - enc4 = _keyStr.indexOf(decodedContent.charAt(i++)); - chr1 = (enc1 << 2) | (enc2 >> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; - output = output + String.fromCharCode(chr1); - if (enc3 !== 64) { - output = output + String.fromCharCode(chr2); - } - - if (enc4 !== 64) { - output = output + String.fromCharCode(chr3); - } - } - - return decodeURIComponent(output); - } - - public decodeStruct(names: string[]): Statement { - let code: string = ` - let ${names[0]} =function (${names[1]}) { - ${names[1]} = decodeURIComponent(${names[1]}); - let ${names[2]} = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',${names[3]} = '',${names[4]} = 0; - - ${names[1]} = ${names[1]}.replace(/[^A-Za-z0-9\\+\\/\\=]/g, ''); - while (${names[4]} < ${names[1]}.length) { - let ${names[5]} = ${names[2]}.indexOf(${names[1]}.charAt(${names[4]}++)); - let ${names[6]} = ${names[2]}.indexOf(${names[1]}.charAt(${names[4]}++)); - let ${names[7]} = ${names[2]}.indexOf(${names[1]}.charAt(${names[4]}++)); - let ${names[8]} = ${names[2]}.indexOf(${names[1]}.charAt(${names[4]}++)); - ${names[3]} += String.fromCharCode((${names[5]} << 2) | (${names[6]} >> 4)); - ${names[3]} += (${names[7]} >> 6 === 1) ? '' : String.fromCharCode(((${names[6]} & 15) << 4) | (${names[7]} >> 2)); - ${names[3]} += (${names[8]} >> 6 === 1) ? '' : String.fromCharCode(((${names[7]} & 3) << 6) | ${names[8]}); - } - return decodeURIComponent(${names[3]}); - } - `; - - let source: SourceFile = createSourceFile('', code, ScriptTarget.ES2015, true); - return NodeUtils.setSynthesis(source.statements[0]); - } -} diff --git a/arkguard/src/utils/NameCacheUtil.ts b/arkguard/src/utils/NameCacheUtil.ts index 5bc78fc4d08a4728e38b1dc8f82aea99363ff86c..bb30910c373c48cb57d63e8b63362ba53876d21c 100644 --- a/arkguard/src/utils/NameCacheUtil.ts +++ b/arkguard/src/utils/NameCacheUtil.ts @@ -23,6 +23,7 @@ export function writeCache(cache: Map, destFileName: string): vo if (!cache) { return; } + const cacheString: string = JSON.stringify(Object.fromEntries(cache)); FileUtils.writeFile(destFileName, cacheString); } diff --git a/arkguard/src/utils/NodeUtils.ts b/arkguard/src/utils/NodeUtils.ts index 36065d9f9c1577712a98b5a7cf39f983c0f9b2c1..11d24da2002733fdd1c2c39da1352c91ffc3ed47 100644 --- a/arkguard/src/utils/NodeUtils.ts +++ b/arkguard/src/utils/NodeUtils.ts @@ -13,22 +13,14 @@ * limitations under the License. */ +import type {Expression, Node, ObjectBindingPattern} from 'typescript'; import { - createPrinter, - EmitHint, - factory, - forEachChild, - isBinaryExpression, isBindingElement, isCallExpression, isComputedPropertyName, isConstructorDeclaration, isElementAccessExpression, isEnumMember, - isExpressionStatement, - isForInStatement, - isForOfStatement, - isForStatement, isGetAccessor, isIdentifier, isMethodDeclaration, @@ -37,49 +29,13 @@ import { isPropertyAccessExpression, isPropertyAssignment, isPropertyDeclaration, - isPropertySignature, isQualifiedName, + isPropertySignature, + isQualifiedName, isSetAccessor, - isStringLiteral, - isTaggedTemplateExpression, isVariableDeclaration, - isWhileStatement, - NodeFlags, - SyntaxKind + isVariableDeclaration } from 'typescript'; -import type { - BinaryExpression, - Block, - ElementAccessExpression, - Expression, - Node, - NodeArray, - ObjectBindingPattern, - Printer, - PrinterOptions, - PropertyAccessExpression, - SourceFile, - Statement, - StringLiteralLike, - VariableDeclaration, - VariableStatement -} from 'typescript'; - -import * as crypto from 'crypto'; - export class NodeUtils { - public static setSynthesis(node: T): T { - visit(node); - return node; - - function visit(node: Node): void { - if (node) { - (node.pos as number) = -1; - (node.end as number) = -1; - forEachChild(node, visit); - } - } - } - public static isPropertyDeclarationNode(node: Node): boolean { let parent: Node | undefined = node.parent; if (!parent) { @@ -129,8 +85,7 @@ export class NodeUtils { return true; } - const result: boolean = isGetAccessor(parent) && parent.name === node; - return result; + return isGetAccessor(parent) && parent.name === node; } public static isPropertyOrElementAccessNode(node: Node): boolean { @@ -147,8 +102,7 @@ export class NodeUtils { if (isPropertyAccessExpression(parent) && parent.name === node) { return true; } - const result: boolean = isQualifiedName(parent) && parent.right === node; - return result; + return isQualifiedName(parent) && parent.right === node; } public static isElementAccessNode(node: Node): boolean { @@ -157,9 +111,7 @@ export class NodeUtils { return false; } - /** eg: a['name'] = 1, pass, a[0] ignore */ - const result: boolean = isElementAccessExpression(parent) && parent.argumentExpression === node; - return result; + return isElementAccessExpression(parent) && parent.argumentExpression === node; } public static isClassPropertyInConstructorParams(node: Node): boolean { @@ -171,7 +123,7 @@ export class NodeUtils { return false; } - return !(!node.parent.parent || !isConstructorDeclaration(node.parent.parent)); + return node.parent.parent && isConstructorDeclaration(node.parent.parent); } public static isClassPropertyInConstructorBody(node: Node, constructorParams: Set): boolean { @@ -200,349 +152,6 @@ export class NodeUtils { return this.isPropertyDeclarationNode(node); } - /** - * let b = { - * 'id' : 'id22' - * } - * let c = ['123'] - * interface tmp1 { - * ['id'] : string; // pass - * // [b.id] : string; // error - * [b['id']]() : string; // error - * }; - * - * enum tmp2{ - * ['id'] = 2, // pass - * [b.id] = 3, // error - * }; - * - * - * let _ = { - * ['id'] : 2, // pass, - * [b.id] : 3, // pass, - * } - * - * interface IPerson { - * 'jfkkf': number, - * ['kkk'] : number - * } - * - * var customer:IPerson = { - * 'jfkkf': 10, - * ['kkk']: 11 - * } - * - * class A { - * - * private ['id'] = 2; // pass - * private [b.id] = 2; // error - * } - * - * class B { - * ['id']() {} - * [c[0]]() { - * } - * } - * - *1. The computable method declaration string of the class can be converted into an array access form; - *2. Computable properties/methods of object literals, which can be converted into array access forms; - *3. Cannot convert to array access form in other forms - * Interface/Type: A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type. - * Enum Computed property names are not allowed in enums. - * ClassDefinition A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. - * @param node - */ - public static isExtractableString(node: StringLiteralLike): boolean { - let parent: Node | undefined = node.parent; - if (!parent) { - return false; - } - - if (isTaggedTemplateExpression(parent)) { - return false; - } - - if (!NodeUtils.isPropertyDeclarationNode(node)) { - return true; - } - - // skip for some situations when in property declaration. - /** let _ = { ['name']: 'jack'} => let _ = {[arr[0]]: 'jack'} */ - if (isComputedPropertyName(parent)) { - let grandparent: Node = parent.parent; - const result: boolean = isMethodDeclaration(grandparent) && grandparent.name === parent; - return result; - } - - return false; - } - - public static randomInsertStatements(statements: Statement[], newStatement: Statement): Statement[] { - let index: number = crypto.randomInt(0, statements.length); - const result: Statement[] = [...statements.slice(0, index), newStatement, ...statements.slice(index, statements.length)]; - return result; - } - - /** - * create array init statement, e.g.: - * const arr = [1,2,3,4]; - * only support string and numeric array - */ - public static createArrayInit(isConst: boolean, varName: string, valueType: SyntaxKind, initArray: string[]): VariableStatement { - let idArr: Expression[] = []; - for (const value of initArray) { - if (valueType === SyntaxKind.StringLiteral) { - idArr.push(factory.createStringLiteral(value)); - } - - if (valueType === SyntaxKind.NumericLiteral) { - idArr.push(factory.createNumericLiteral(value)); - } - } - - const declaration: VariableDeclaration = factory.createVariableDeclaration( - factory.createIdentifier(varName), - undefined, - undefined, - factory.createArrayLiteralExpression(idArr, false) - ); - - return factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList([declaration], NodeFlags.Const) - ); - } - - /** - * create numeric variable declaration with random value - * const varName = Math.floor(Math.random() * (max - min) + min); - * @return integer random value in range [min, max] - */ - public static createNumericWithRandom(varName: string, min: number, max: number): VariableStatement { - let innerBinary: BinaryExpression = factory.createBinaryExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('Math'), - factory.createIdentifier('random') - ), - undefined, - [] - ), - SyntaxKind.AsteriskToken, - factory.createNumericLiteral(max - min) - ); - - if (min !== 0) { - innerBinary = factory.createBinaryExpression( - innerBinary, - SyntaxKind.PlusToken, - factory.createNumericLiteral(min) - ); - } - - const declaration: VariableDeclaration = factory.createVariableDeclaration( - factory.createIdentifier(varName), - undefined, - undefined, - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('Math'), - factory.createIdentifier('floor') - ), - undefined, - [ - innerBinary - ] - ) - ); - - return factory.createVariableStatement( - null, - factory.createVariableDeclarationList([declaration], NodeFlags.Const) - ); - } - - /** - * create variable lower expression: (x | 0) - * @private - */ - public static createLowerExpression(expression: Expression): Expression { - return factory.createParenthesizedExpression( - factory.createBinaryExpression( - {...expression}, - SyntaxKind.BarToken, - factory.createNumericLiteral('0') - ) - ); - } - - /** - * create trunc expression: Math.trunc(x) - */ - public static createTruncExpression(expression: Expression): Expression { - return factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('Math'), - factory.createIdentifier('trunc') - ), - undefined, - [ - {...expression} - ] - ); - } - - /** - * change property access expression to element access expression - * example: - * console.log() -> console['log']() - */ - public static changePropertyAccessToElementAccess(expression: PropertyAccessExpression): ElementAccessExpression { - return factory.createElementAccessExpression( - {...expression.expression}, - factory.createStringLiteral(expression.name.escapedText.toString()) - ); - } - - public static isMostInnerBinary(node: Node): boolean { - let flag: boolean = true; - forEachChild(node, (child) => { - if (!flag) { - return; - } - - if (this.hasBinary(child)) { - flag = false; - return; - } - }); - - return flag; - } - - private static hasBinary(node: Node): boolean { - let flag: boolean = false; - let visit = (inputNode): void => { - if (flag) { - return; - } - - if (isBinaryExpression(inputNode)) { - flag = true; - return; - } - - forEachChild(inputNode, visit); - }; - - visit(node); - return flag; - } - - public static isMostInnerCallExpression(node: Node): boolean { - let flag: boolean = true; - forEachChild(node, (child) => { - if (!flag) { - return; - } - - if (this.hasCallExpression(child)) { - flag = false; - return; - } - }); - - return flag; - } - - private static hasCallExpression(node: Node): boolean { - let flag: boolean = false; - let visit = (inputNode): void => { - if (flag) { - return; - } - - if (isCallExpression(inputNode)) { - flag = true; - return; - } - - forEachChild(inputNode, visit); - }; - - visit(node); - return flag; - } - - public static isContainNarrowNames(node: Node, narrowNames: string[]): boolean { - let flag: boolean = false; - forEachChild(node, (child) => { - if (flag) { - return; - } - - if (this.hasNarrowNames(child, narrowNames)) { - flag = true; - return; - } - }); - - return flag; - } - - private static hasNarrowNames(node: Node, narrowNames: string[]): boolean { - let flag: boolean = false; - let visit = (inputNode: Node): void => { - if (flag) { - return; - } - - if (isIdentifier(inputNode) && - narrowNames.includes(inputNode.text)) { - flag = true; - return; - } - - if (isStringLiteral(inputNode) && - narrowNames.includes(inputNode.text)) { - flag = true; - return; - } - - forEachChild(inputNode, visit); - }; - - visit(node); - return flag; - } - - public static isContainForbidStringStatement(node: Block): boolean { - let result: boolean = false; - let statements: NodeArray = node.statements; - - statements?.forEach((st: Statement) => { - if (isExpressionStatement(st) && isStringLiteral(st.expression)) { - result = true; - } - }); - - return result; - } - - public static printNode(node: Node, sourceFile: SourceFile): string { - const printOptions: PrinterOptions = {}; - const printer: Printer = createPrinter(printOptions); - - return printer.printNode(EmitHint.Unspecified, node, sourceFile); - } - - public static isLoopStatement(node: Node): boolean { - return isForStatement(node) || - isForInStatement(node) || - isForOfStatement(node) || - isWhileStatement(node); - } - public static isObjectBindingPatternAssignment(node: ObjectBindingPattern): boolean { if (!node || !node.parent || !isVariableDeclaration(node.parent)) { return false; diff --git a/arkguard/src/utils/OhsUtil.ts b/arkguard/src/utils/OhsUtil.ts index 5ae093fd5cd2386ecc77dbea22312fbd9e304cac..4ffcc5918033118fe52d544ebdd886df382a264c 100644 --- a/arkguard/src/utils/OhsUtil.ts +++ b/arkguard/src/utils/OhsUtil.ts @@ -21,15 +21,23 @@ import { isPropertyAccessExpression, isStringLiteral, isVariableStatement, - SyntaxKind + SyntaxKind, + isPropertyDeclaration, + isObjectLiteralExpression, + isEnumDeclaration, + isPropertyAssignment, + StructDeclaration, + isStructDeclaration } from 'typescript'; import type { ClassDeclaration, + ClassExpression, + EnumDeclaration, Expression, HeritageClause, - Identifier, NodeArray, + ObjectLiteralExpression, Statement } from 'typescript'; @@ -121,65 +129,54 @@ export function findOhImportStatement(node: Statement, moduleName: string): OhPa return OhPackType.NONE; } - function containViewPU(heritageClauses: NodeArray): boolean { if (!heritageClauses) { return false; } + let hasViewPU: boolean = false; heritageClauses.forEach( (heritageClause) => { if (!heritageClause || !heritageClause.types) { return; } + const types = heritageClause.types; types.forEach((typeExpression) => { if (!typeExpression || !typeExpression.expression) { return; } + const expression = typeExpression.expression; if (isIdentifier(expression) && expression.text === 'ViewPU') { hasViewPU = true; } }); - }); - return hasViewPU; -} + }); -/** - * used to ignore user defined ui component class name - * @param nameNode - */ -export function isViewPUBasedClassName(nameNode: Identifier): boolean { - if (!nameNode || !nameNode.parent) { - return false; - } - if (!isClassDeclaration(nameNode.parent)) { - return false; - } - const heritageClause = nameNode.parent.heritageClauses; - return containViewPU(heritageClause); + return hasViewPU; } /** * used to ignore user defined ui component class property name - * @param nameNode + * @param classNode */ export function isViewPUBasedClass(classNode: ClassDeclaration): boolean { if (!classNode) { return false; } + if (!isClassDeclaration(classNode)) { return false; } + const heritageClause = classNode.heritageClauses; return containViewPU(heritageClause); } -export function getClassProperties(classNode: ClassDeclaration): Set { - const properties: Set = new Set(); +export function getClassProperties(classNode: ClassDeclaration | ClassExpression | StructDeclaration, propertySet: Set): void { if (!classNode || !classNode.members) { - return properties; + return; } classNode.members.forEach((member) => { @@ -188,13 +185,102 @@ export function getClassProperties(classNode: ClassDeclaration): Set { } if (isIdentifier(member.name)) { - properties.add(member.name.text); + propertySet.add(member.name.text); } if (isStringLiteral(member.name)) { - properties.add(member.name.text); + propertySet.add(member.name.text); + } + + //extract class member's property, example: export class hello {info={read: {}}} + if (isClassDeclaration(classNode) && isViewPUBasedClass(classNode)) { + return; + } + + if (!isPropertyDeclaration(member) || !member.initializer) { + return; + } + + if (isObjectLiteralExpression(member.initializer)) { + getObjectProperties(member.initializer, propertySet); + return; + } + + if (isClassDeclaration(member.initializer) || isStructDeclaration(member.initializer)) { + getClassProperties(member.initializer, propertySet); + return; + } + + if (isEnumDeclaration(member.initializer)) { + getEnumProperties(member.initializer, propertySet); + return; + } + }); + + return; +} + +export function getEnumProperties(enumNode: EnumDeclaration, propertySet: Set): void { + if (!enumNode || !enumNode.members) { + return; + } + + enumNode.members.forEach((member) => { + if (!member || !member.name) { + return; + } + + if (isIdentifier(member.name)) { + propertySet.add(member.name.text); + } + + if (isStringLiteral(member.name)) { + propertySet.add(member.name.text); } //other kind ignore }); - return properties; -} \ No newline at end of file + + return; +} + +export function getObjectProperties(objNode: ObjectLiteralExpression, propertySet: Set): void { + if (!objNode || !objNode.properties) { + return; + } + + objNode.properties.forEach((propertyElement) => { + if (!propertyElement || !propertyElement.name) { + return; + } + + if (isIdentifier(propertyElement.name)) { + propertySet.add(propertyElement.name.text); + } + + if (isStringLiteral(propertyElement.name)) { + propertySet.add(propertyElement.name.text); + } + + //extract class element's property, example: export const hello = {info={read: {}}} + if (!isPropertyAssignment(propertyElement) || !propertyElement.initializer) { + return; + } + + if (isObjectLiteralExpression(propertyElement.initializer)) { + getObjectProperties(propertyElement.initializer, propertySet); + return; + } + + if (isClassDeclaration(propertyElement.initializer)) { + getClassProperties(propertyElement.initializer, propertySet); + return; + } + + if (isEnumDeclaration(propertyElement.initializer)) { + getEnumProperties(propertyElement.initializer, propertySet); + return; + } + }); + + return; +} diff --git a/arkguard/src/utils/ScopeAnalyzer.ts b/arkguard/src/utils/ScopeAnalyzer.ts index 51ae011a0a8f9810524f915a888bbe7858a1e3be..abb71ec030c5887759faf3f644d82e7b58105a3b 100644 --- a/arkguard/src/utils/ScopeAnalyzer.ts +++ b/arkguard/src/utils/ScopeAnalyzer.ts @@ -303,7 +303,7 @@ namespace secharmony { /** * get reserved names like ViewPU component class name */ - getReservedNames(): Set + getReservedNames(): Set; /** * do scope analysis * diff --git a/arkguard/src/utils/TransformUtil.ts b/arkguard/src/utils/TransformUtil.ts index 9b888880e67aec4a86fbf9c09aa199531b687635..c951f5f1f1aac7c48a30af73387874cf15f949f6 100644 --- a/arkguard/src/utils/TransformUtil.ts +++ b/arkguard/src/utils/TransformUtil.ts @@ -25,13 +25,12 @@ import { import type { CommentRange, - Expression, Identifier, Node, SourceFile, - Statement, TransformationContext } from 'typescript'; + /** * collect exist identifier names in current source file * @param sourceFile @@ -72,35 +71,12 @@ export function collectIdentifiers(sourceFile: SourceFile, context: Transformati return identifiers; } -/** - * is current node contain ignore obfuscation comment - * comment: // @skipObfuscate - */ -export function isObfsIgnoreNode(node: Node, sourceFile: SourceFile): boolean { - const ranges: CommentRange[] = getLeadingCommentRangesOfNode(node, sourceFile); - if (!ranges) { - return false; - } - - const ignoreComment: string = '//@skipObfuscate'; - for (const range of ranges) { - const comment: string = sourceFile.text.slice(range.pos, range.end).replace(' ', '').replace('\t', ''); - if (comment === ignoreComment) { - return true; - } - } - - return false; -} - export enum OhPackType { NONE, JS_BUNDLE, ES_MODULE } - - export function isCommentedNode(node: Node, sourceFile: SourceFile): boolean { const ranges: CommentRange[] = getLeadingCommentRangesOfNode(node, sourceFile); return ranges !== undefined; diff --git a/arkguard/src/utils/TypeUtils.ts b/arkguard/src/utils/TypeUtils.ts index 8262a2f6198cf4dec0a38a07d5146be05c0cd938..eb477729561c661174ddaecef2b70cf279d8d3b7 100644 --- a/arkguard/src/utils/TypeUtils.ts +++ b/arkguard/src/utils/TypeUtils.ts @@ -29,6 +29,7 @@ import type { SourceFile, TypeChecker, } from 'typescript'; + import path from 'path'; export class TypeUtils { @@ -40,6 +41,7 @@ export class TypeUtils { public static createNewSourceFile(oldAst: SourceFile): SourceFile { let printer: Printer = createPrinter(); let content: string = printer.printFile(oldAst); + const fileSuffix: string = '.ts'; const { dir, name } = path.parse(oldAst.fileName); const targetName: string = path.join(dir, name) + '__tmp' + fileSuffix; diff --git a/arkguard/test/grammar/advanced_type/discriminated_unions.ts b/arkguard/test/grammar/advanced_type/discriminated_unions.ts index d420320da72ac1e3760aeb3cd740b4bb4ee9b423..a2c9d3a9d0fb296bd932f1ba1a0414f0a83a66fd 100644 --- a/arkguard/test/grammar/advanced_type/discriminated_unions.ts +++ b/arkguard/test/grammar/advanced_type/discriminated_unions.ts @@ -52,4 +52,4 @@ function area(s: Shape): number { let a = {kind: 'square', size: 3}; const targetArea: number = 9; -assert(area(a) === targetArea, 'success'); \ No newline at end of file +assert(area(a) === targetArea, 'success'); diff --git a/arkguard/src/transformers/bogus/AbstractBogusControlHelper.ts b/arkguard/test/grammar/class_validation/class_demo_1.ts similarity index 55% rename from arkguard/src/transformers/bogus/AbstractBogusControlHelper.ts rename to arkguard/test/grammar/class_validation/class_demo_1.ts index 768a6bea748ac05e0eb8c24a92f2ffbd87178cbb..557d5dfbeb1b332e2abda56d3c98fe1e5c706ef9 100644 --- a/arkguard/src/transformers/bogus/AbstractBogusControlHelper.ts +++ b/arkguard/test/grammar/class_validation/class_demo_1.ts @@ -13,20 +13,37 @@ * limitations under the License. */ -import type {Block, Statement} from 'typescript'; +import assert = require('assert'); -export abstract class AbstractBogusControlHelper { - protected mOriginalUnits: Statement[]; - protected mUseOpaquePredicate: boolean; +export class Cat { + private mAge: number; - protected constructor(units: Statement[], useOpaquePredicate: boolean) { - this.mOriginalUnits = units; - this.mUseOpaquePredicate = useOpaquePredicate; + constructor() { + this.mAge = -1; } - public abstract getNewBlock(bogusBlock: Block): Block; + getAge() { + return this.mAge; + } + + setAge(age) { + this.mAge = age; + } + + get age(): number { + return this.mAge; + } - public getBogusStruct(bogusBlock: Block): Block { - return this.getNewBlock(bogusBlock); + set age(_age) { + this.mAge = _age; } } + +const cat = new Cat(); +assert.strictEqual(cat.getAge(), -1); + +cat.age = 12; +assert.strictEqual(cat.age, 12); + +cat.setAge(13); +assert.strictEqual(cat.age, 13); diff --git a/arkguard/src/configs/ArkGuardOptions.ts b/arkguard/test/grammar/class_validation/class_demo_2.ts similarity index 50% rename from arkguard/src/configs/ArkGuardOptions.ts rename to arkguard/test/grammar/class_validation/class_demo_2.ts index ced1afce585109aea81c094a0598825b46ad62d0..e84e68a5c6bfa6bf99bf47b43cad9bcd9e3c16c9 100644 --- a/arkguard/src/configs/ArkGuardOptions.ts +++ b/arkguard/test/grammar/class_validation/class_demo_2.ts @@ -13,40 +13,36 @@ * limitations under the License. */ -import type {IOptions} from './IOptions'; - -export interface ArkGuardOptions { - /** - * path of open harmony sdk - */ - sdkPath?: string, - - /** - * api version of open harmony sdk - */ - apiVersion?: string, - - /** - * Protection level specified by user - */ - protectedLevel?: boolean; - - /** - * whether to turn on source map function. - */ - sourceMap?: boolean, - /** - * specify the path of name cache file, use in hot update. - */ - nameCache?: string, - - /** - * output directory for obfuscated results. - */ - outDir?: string, - - /** - * obfuscation options - */ - obfuscations?: IOptions, +import assert = require('assert'); + +const Cat = "cat"; +const Dog = "dog"; + +class MyPets{ + [Cat] = "tom"; + [Dog] = "spike"; + + constructor(public para1 = 1, private para2 = "hello") { + this.para1 = 5; + } + + public getPara1(){ + return this.para1; + } + + public getPara2(){ + return this.para2; + } } + +const pet = new MyPets(); + +assert(MyPets[Cat]===undefined); +assert(MyPets[Dog]===undefined); + +assert(pet[Cat]==="tom"); +assert(pet[Dog]==="spike"); + +assert.strictEqual(pet.getPara1(), 5); +assert.strictEqual(pet.getPara2(), "hello"); +assert.strictEqual(pet.para1, 5); diff --git a/arkguard/src/configs/IHideOhApiOption.ts b/arkguard/test/grammar/class_validation/class_demo_3.ts similarity index 76% rename from arkguard/src/configs/IHideOhApiOption.ts rename to arkguard/test/grammar/class_validation/class_demo_3.ts index ef4ee5389b45971d394c1290b77d639a64c0e0fd..6d473fe5f79eb53b15cc4164df5f8b2fe84f61ba 100644 --- a/arkguard/src/configs/IHideOhApiOption.ts +++ b/arkguard/test/grammar/class_validation/class_demo_3.ts @@ -13,12 +13,16 @@ * limitations under the License. */ -export interface IHideOhApiOption { - readonly mEnable: boolean; +const cat = new class { + private readonly mAge: number; - /** - * openHarmony api list to be hidden, like: - * - @ohos.hilog.info - */ - readonly mProtectedApi: string[]; + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } } + +export = cat; diff --git a/arkguard/test/grammar/class_validation/class_demo_3_use.ts b/arkguard/test/grammar/class_validation/class_demo_3_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b111c276f81f268c9a1db652fb24b5cbac44017 --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_demo_3_use.ts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const d = require('./class_demo_3'); +assert.strictEqual(d.getAge(), 2); diff --git a/arkguard/test/grammar/class_validation/class_demo_4.ts b/arkguard/test/grammar/class_validation/class_demo_4.ts new file mode 100644 index 0000000000000000000000000000000000000000..8db9eb06ba39ba81bdd011b3f7146fa963b62a4d --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_demo_4.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const cat = new class { + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } +} diff --git a/arkguard/test/grammar/class_validation/class_demo_4_use.ts b/arkguard/test/grammar/class_validation/class_demo_4_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..75b987d60426662615f786a5468050e9aa59f3a5 --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_demo_4_use.ts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const d = require('./class_demo_4'); +assert.strictEqual(d.cat.getAge(), 2); diff --git a/arkguard/test/grammar/class_validation/class_demo_5.ts b/arkguard/test/grammar/class_validation/class_demo_5.ts new file mode 100644 index 0000000000000000000000000000000000000000..b32a7bfd2612a1d91a1bd900b20faf304c2d5257 --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_demo_5.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Cat { + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } +} + +export {Cat as CAT}; diff --git a/arkguard/test/grammar/class_validation/class_demo_5_use.ts b/arkguard/test/grammar/class_validation/class_demo_5_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..8932ac99ad35eef1ca4bc4e67142eda827249700 --- /dev/null +++ b/arkguard/test/grammar/class_validation/class_demo_5_use.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const Cat = require('./class_demo_5'); + +const cat = new Cat.CAT(); +assert.strictEqual(cat.getAge(), 2); diff --git a/arkguard/src/transformers/data/BoolObfuscationHelper.ts b/arkguard/test/grammar/class_validation/class_in_namespace.ts similarity index 40% rename from arkguard/src/transformers/data/BoolObfuscationHelper.ts rename to arkguard/test/grammar/class_validation/class_in_namespace.ts index 06fc296eb9babda275666f3b9b52d1da97cd8a3a..b6d82bc22fe3a44c903e30649229ca4cdaf0401d 100644 --- a/arkguard/src/transformers/data/BoolObfuscationHelper.ts +++ b/arkguard/test/grammar/class_validation/class_in_namespace.ts @@ -13,32 +13,61 @@ * limitations under the License. */ -import {factory, SyntaxKind} from 'typescript'; -import type {Expression, Node} from 'typescript'; +const assert = require('assert'); -export class BoolObfuscationHelper { - public static isBooleanLiteral(node: Node): boolean { - return node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword; - } +export namespace secHello { + export namespace secWorld { + export function sayWorld() { + const instance = new World(); + return instance.sayWorld(); + } + + class World{ + private readonly worldStr; + + constructor() { + this.worldStr = "world"; + } - public static isTrueKeyword(node: Node): boolean { - return node.kind === SyntaxKind.TrueKeyword; + public sayWorld(){ + return this.worldStr; + } + } } - public static createTrueObfuscation(): Expression { - return factory.createPrefixUnaryExpression( - SyntaxKind.ExclamationToken, - factory.createPrefixUnaryExpression( - SyntaxKind.ExclamationToken, - factory.createArrayLiteralExpression() - ) - ); + assert.strictEqual(secWorld.sayWorld(), "world"); + + export class Hello { + private readonly helloStr; + + constructor() { + this.helloStr = 'hello'; + } + + public sayHello(){ + return this.helloStr; + } } + + function sayHello() { + + } + + export const cat = new class{ + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } - public static createFalseObfuscation(): Expression { - return factory.createPrefixUnaryExpression( - SyntaxKind.ExclamationToken, - factory.createArrayLiteralExpression() - ); + getAge(){ + return this.mAge; + } } } + +const hello = new secHello.Hello(); + +assert.strictEqual(hello.sayHello(), "hello"); + +assert.strictEqual(secHello.cat.getAge(), 2); diff --git a/arkguard/test/grammar/enum/computed_enum 2.ts b/arkguard/test/grammar/enum/computed_enum 2.ts new file mode 100644 index 0000000000000000000000000000000000000000..8afa1dda90e43a7f4fb1fffd2f8917539aecab56 --- /dev/null +++ b/arkguard/test/grammar/enum/computed_enum 2.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +enum ANIMAL { + CAT, + DOG = 1 + (CAT + 1), +} + +const favoriteType1 = ANIMAL.CAT; +assert.strictEqual(favoriteType1, ANIMAL.CAT); + +const favoriteType2 = ANIMAL.DOG; +assert.strictEqual(favoriteType2, ANIMAL.DOG); + diff --git a/arkguard/test/grammar/enum/computed_enum.ts b/arkguard/test/grammar/enum/computed_enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..efa5f02b6b70f6c03b30c2d9727f764710cbad57 --- /dev/null +++ b/arkguard/test/grammar/enum/computed_enum.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +enum ANIMAL { + CAT, + DOG = CAT + 1, + GOOSE = DOG + 1, + DUCK = GOOSE + 1, +} + +const favoriteType1 = ANIMAL.CAT; +assert.strictEqual(favoriteType1, ANIMAL.CAT); + +const favoriteType2 = ANIMAL.DOG; +assert.strictEqual(favoriteType2, ANIMAL.DOG); + +const favoriteType3 = ANIMAL.GOOSE; +assert.strictEqual(favoriteType3, ANIMAL.GOOSE); + +const favoriteType4 = ANIMAL.DUCK; +assert.strictEqual(favoriteType4, ANIMAL.DUCK); diff --git a/arkguard/test/grammar/enum/enum_use.ts b/arkguard/test/grammar/enum/enum_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..8bb192aa2666cfe3bae5e030d0f88c3635fb05b0 --- /dev/null +++ b/arkguard/test/grammar/enum/enum_use.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import assert = require('assert'); + +enum ANIMAL { + CAT, + DOG, + GOOSE, + DUCK, +} + +const favoriteType1 = ANIMAL.CAT; +assert.strictEqual(favoriteType1, ANIMAL.CAT); + +const favoriteType2 = ANIMAL.DOG; +assert.strictEqual(favoriteType2, ANIMAL.DOG); + +const favoriteType3 = ANIMAL.GOOSE; +assert.strictEqual(favoriteType3, ANIMAL.GOOSE); + +const favoriteType4 = ANIMAL.DUCK; +assert.strictEqual(favoriteType4, ANIMAL.DUCK); diff --git a/arkguard/test/grammar/export/export_demo_1.ts b/arkguard/test/grammar/export/export_demo_1.ts new file mode 100644 index 0000000000000000000000000000000000000000..954c62c45dd37aceb8de921228863ce8cf60cbc2 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_1.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Cat { + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } +} + +export {Cat}; diff --git a/arkguard/test/grammar/export/export_demo_11.ts b/arkguard/test/grammar/export/export_demo_11.ts new file mode 100644 index 0000000000000000000000000000000000000000..499264211da063605519cf5fdcefefa0310626c5 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_11.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Cat { + private readonly mAge: number; + constructor() { + this.mAge = 3; + } + + getAge() { + return this.mAge; + } +} + +export {Cat as Dog}; \ No newline at end of file diff --git a/arkguard/test/grammar/export/export_demo_11_use.ts b/arkguard/test/grammar/export/export_demo_11_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..a6850d1334195c444b43f69fc5a917c92c3b7ade --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_11_use.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const Cat1 = require('./export_demo_11'); + +const cat1 = new Cat1.Dog(); + +assert.strictEqual(cat.getAge(), 3); diff --git a/arkguard/test/grammar/export/export_demo_12.ts b/arkguard/test/grammar/export/export_demo_12.ts new file mode 100644 index 0000000000000000000000000000000000000000..944bc107124fe50b28bf994f87e9d61fb823c09e --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_12.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default class { + private readonly mAge: number; + constructor() { + this.mAge = 4; + } + + getAge() { + return this.mAge; + } + +} diff --git a/arkguard/test/grammar/export/export_demo_12_use.ts b/arkguard/test/grammar/export/export_demo_12_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..a6909cc5e56fb547e3b0164ce707ce0fe78ad53d --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_12_use.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const Cat2 = require('./export_demo_12'); + +const cat2 = new Cat2.default(); + +assert.strictEqual(cat2.getAge(), 4); diff --git a/arkguard/test/grammar/export/export_demo_1_use.ts b/arkguard/test/grammar/export/export_demo_1_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..14bab35ad0ec87753df0f2128b8a334c2a5de914 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_1_use.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const Cat = require('./export_demo_1'); + +const cat = new Cat.Cat(); +assert.strictEqual(cat.getAge(), 2); diff --git a/arkguard/test/grammar/export/export_demo_2.ts b/arkguard/test/grammar/export/export_demo_2.ts new file mode 100644 index 0000000000000000000000000000000000000000..d5bcae1d04d419128768da8610ef12d365c9ebfe --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_2.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Cat { + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } +} + +export = Cat; diff --git a/arkguard/test/grammar/export/export_demo_2_use.ts b/arkguard/test/grammar/export/export_demo_2_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..0181f8d59f64d26d68476c1aca5381e14aacbcb2 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_2_use.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const Cat = require('./export_demo_2'); + +const cat = new Cat(); +assert.strictEqual(cat.getAge(), 2); diff --git a/arkguard/test/grammar/export/export_demo_3.ts b/arkguard/test/grammar/export/export_demo_3.ts new file mode 100644 index 0000000000000000000000000000000000000000..3784e4b1692ff0f00d5297a029402e815adf181a --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_3.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default class { + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } +} diff --git a/arkguard/test/grammar/export/export_demo_3_use.ts b/arkguard/test/grammar/export/export_demo_3_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..80bc1014f9539eba13cb818f5f616c9e68d32ef5 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_3_use.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const Cat = require('./export_demo_3'); + +const cat = new Cat.default(); +assert.strictEqual(cat.getAge(), 2); diff --git a/arkguard/src/configs/IInstructionObfuscationOption.ts b/arkguard/test/grammar/export/export_demo_4.ts similarity index 57% rename from arkguard/src/configs/IInstructionObfuscationOption.ts rename to arkguard/test/grammar/export/export_demo_4.ts index e6e6fd458b9097ebe7dfbf22044377e10327b220..ec047043e52545c017682afccb583d58ccc0a4b5 100644 --- a/arkguard/src/configs/IInstructionObfuscationOption.ts +++ b/arkguard/test/grammar/export/export_demo_4.ts @@ -13,27 +13,23 @@ * limitations under the License. */ -export enum InstructionObfsMethod { - /** - * use simple deformation to instruction - */ - SIMPLE_DEFORM = 1, - - /** - * transform instruction to MBA expression - */ - MBA_EXPRESSION = 2, +const Dog = { + age: 2, + getAge(){ + return this.age; + } } -export interface IInstructionObfuscationOption { - readonly mEnable: boolean; - - readonly mThreshold: number; +const Cat = class { + private readonly mAge: number; - /** - * skip obfuscation in loop for performance - */ - readonly mSkipLoop: boolean; + constructor() { + this.mAge = 2; + } - readonly mInstructionObfsMethod: InstructionObfsMethod; + getAge() { + return this.mAge; + } } + +export = Cat; \ No newline at end of file diff --git a/arkguard/test/grammar/export/export_demo_4_use.ts b/arkguard/test/grammar/export/export_demo_4_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..1bcc498e8828a3061dfa48d6d28bfdd129223504 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_4_use.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const Cat = require('./export_demo_4'); + +const cat = new Cat(); +assert.strictEqual(cat.getAge(), 2); diff --git a/arkguard/test/grammar/export/export_demo_5.ts b/arkguard/test/grammar/export/export_demo_5.ts new file mode 100644 index 0000000000000000000000000000000000000000..9b12b4892d81f487bacf3d090d39523b87eaaae9 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_5.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const Dog = { + age: 2, + getAge(){ + return this.age; + } +} + +const Cat = class { + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } +} + +export = Dog; \ No newline at end of file diff --git a/arkguard/test/grammar/export/export_demo_5_use.ts b/arkguard/test/grammar/export/export_demo_5_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b921e278d78ba90f1463dc5060ec49971688f18 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_5_use.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const dog = require('./export_demo_5'); + +assert.strictEqual(dog.getAge(), 2); diff --git a/arkguard/test/grammar/export/export_demo_6.ts b/arkguard/test/grammar/export/export_demo_6.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b833d663d99e981423476e38f609180061420d1 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_6.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const Dog = { + age: 2, + getAge(){ + return this.age; + } +} + +const Cat = class { + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } +} diff --git a/arkguard/test/grammar/export/export_demo_6_use.ts b/arkguard/test/grammar/export/export_demo_6_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..58b24b3872f31df1d3ed5b67f5e9533bea48b3b2 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_6_use.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const dog = require('./export_demo_6'); + +assert.strictEqual(dog.Dog.getAge(), 2); diff --git a/arkguard/test/grammar/export/export_demo_7.ts b/arkguard/test/grammar/export/export_demo_7.ts new file mode 100644 index 0000000000000000000000000000000000000000..59e5d2b4f5cebb9d71a6881c90d2c9420daf8ad4 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_7.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const Dog = { + age: 2, + getAge(){ + return this.age; + } +} + +export const Cat = class { + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } +} diff --git a/arkguard/test/grammar/export/export_demo_7_use.ts b/arkguard/test/grammar/export/export_demo_7_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..1c5258746e85d0a66e055ef478540b427f366ae7 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_7_use.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const Cat = require('./export_demo_7'); + +const cat = new Cat.Cat(); + +assert.strictEqual(cat.getAge(), 2); diff --git a/arkguard/test/grammar/export/export_demo_8.ts b/arkguard/test/grammar/export/export_demo_8.ts new file mode 100644 index 0000000000000000000000000000000000000000..66a40b61ccdd2f4fd2b30b5c4cba61239672b715 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_8.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +let Dog; +export default Dog = { + age: 2, + getAge(){ + return this.age; + } +} + +const Cat = class { + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } +} diff --git a/arkguard/test/grammar/export/export_demo_8_use.ts b/arkguard/test/grammar/export/export_demo_8_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..d0eafb2bba407712e5a008d8e5c1b3a6b326283b --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_8_use.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const Dog = require('./export_demo_8'); + +assert.strictEqual(Dog.default.getAge(), 2); diff --git a/arkguard/test/grammar/export/export_demo_9.ts b/arkguard/test/grammar/export/export_demo_9.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f1637f1df612a103fb0efa49f5d3b0d3858d0f1 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_9.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const Dog = { + age: 2, + getAge(){ + return this.age; + } +} + +let Cat; +export default Cat = class { + private readonly mAge: number; + + constructor() { + this.mAge = 2; + } + + getAge() { + return this.mAge; + } +} diff --git a/arkguard/test/grammar/export/export_demo_9_use.ts b/arkguard/test/grammar/export/export_demo_9_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..05632a18c9e88c8cb630663b065084659d58d5b1 --- /dev/null +++ b/arkguard/test/grammar/export/export_demo_9_use.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const Cat = require('./export_demo_9'); + +const cat = new Cat.default(); + +assert.strictEqual(cat.getAge(), 2); diff --git a/arkguard/test/grammar/obj/obj_demo_1.ts b/arkguard/test/grammar/obj/obj_demo_1.ts new file mode 100644 index 0000000000000000000000000000000000000000..d40b4db0a13b437b19c27fe1fa3ca130bb4ae3eb --- /dev/null +++ b/arkguard/test/grammar/obj/obj_demo_1.ts @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const Dog = { + age: 2, + getAge(){ + return this.age; + } +}; + +export = Dog; \ No newline at end of file diff --git a/arkguard/test/grammar/obj/obj_demo_1_use.ts b/arkguard/test/grammar/obj/obj_demo_1_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..484967828983b7a93330de19e234f844bf4102dd --- /dev/null +++ b/arkguard/test/grammar/obj/obj_demo_1_use.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const dog = require('./obj_demo_1'); + +assert.strictEqual(dog.getAge(), 2); diff --git a/arkguard/test/grammar/obj/obj_demo_2.ts b/arkguard/test/grammar/obj/obj_demo_2.ts new file mode 100644 index 0000000000000000000000000000000000000000..b0d2c16f8194c8085a5f1bf4ab7442b428507870 --- /dev/null +++ b/arkguard/test/grammar/obj/obj_demo_2.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const Dog = { + age: 2, + 'height': 10, + getAge(){ + return this.age; + } +}; + +export = Dog; \ No newline at end of file diff --git a/arkguard/test/grammar/obj/obj_demo_2_use.ts b/arkguard/test/grammar/obj/obj_demo_2_use.ts new file mode 100644 index 0000000000000000000000000000000000000000..901eb95f136927a91d85a85cdefcf04ad299e5ca --- /dev/null +++ b/arkguard/test/grammar/obj/obj_demo_2_use.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const dog = require('./obj_demo_2'); + +assert.strictEqual(dog.height, 10); diff --git a/arkguard/test/grammar/obj/obj_demo_3 use.ts b/arkguard/test/grammar/obj/obj_demo_3 use.ts new file mode 100644 index 0000000000000000000000000000000000000000..f5b75b6e73fccc9fb5e2fc0ede28ee50b450c633 --- /dev/null +++ b/arkguard/test/grammar/obj/obj_demo_3 use.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const assert = require('assert'); + +const dog2 = require('./obj_demo_3'); + +assert.strictEqual(dog2.getAge(), 2); \ No newline at end of file diff --git a/arkguard/test/grammar/obj/obj_demo_3.ts b/arkguard/test/grammar/obj/obj_demo_3.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab2e4e13580ec27d834a4140a53732905917ef39 --- /dev/null +++ b/arkguard/test/grammar/obj/obj_demo_3.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default { + age: 2, + getAge() { + return this.age; + } +} \ No newline at end of file diff --git a/arkguard/test/ut/bogus/OpaquePredicate.spec.ts b/arkguard/test/ut/bogus/OpaquePredicate.spec.ts deleted file mode 100644 index 038ec77007564d668a34dc3bcee410626f196dd3..0000000000000000000000000000000000000000 --- a/arkguard/test/ut/bogus/OpaquePredicate.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import {describe, it} from 'mocha'; -import {assert} from 'chai'; - -describe('opaque predicate test', function () { - const maxValueFirst = 125; - it('test y < 10 || x * (x + 1) % 2 == 0;', function () { - for (let i = 0; i <= maxValueFirst; i++) { - for (let j = 0; j <= maxValueFirst; j++) { - assert.isTrue(j < 10 || i * (i + 1) % 2 === 0); - } - } - }); - - it('test 7* x* x − y* y != 1 || y < n;', function () { - for (let i = 0; i <= maxValueFirst; i++) { - for (let j = 0; j <= maxValueFirst; j++) { - assert.isTrue(7 * i * i - j * j !== 1); - } - } - }); - - const maxValueSecond = 10000; - it('test (4*x*x + 4) mod 19 != 0;', function () { - for (let i = 0; i <= maxValueSecond; i++) { - assert.isTrue((4 * i * i + 4) % 19 !== 0); - } - }); - - it('test (x*x + x +7) % 81 != 0;', function () { - for (let i = 0; i <= maxValueSecond; i++) { - assert.isTrue((i * i + i + 7) % 81 !== 0); - } - }); - - it('test (x*x*x - x) % 3 == 0;', function () { - for (let i = 0; i <= maxValueSecond; i++) { - assert.isTrue((i * i * i - i) % 3 === 0); - } - }); -}); diff --git a/arkguard/test/ut/utils/ListUtil.spec.ts b/arkguard/test/ut/utils/ListUtil.spec.ts index f4c023f1f4dccce8b9efb7d117156381bff6ec6e..6b5b7795afd2ea638ed36eec80237c40ba8e33b3 100644 --- a/arkguard/test/ut/utils/ListUtil.spec.ts +++ b/arkguard/test/ut/utils/ListUtil.spec.ts @@ -65,10 +65,14 @@ describe('unit test for ListUtil.ts', function () { let arr = ListUtil.getInitList(26); ListUtil.shuffle(arr); + let isShuffled = false; for (let i = 1; i < arr.length; i++) { - const isShuffled = arr[i] !== i || Math.abs(arr[i - 1] - arr[i]) > 1; - assert.isTrue(isShuffled); + if (arr[i] !== i) { + isShuffled = true; + } } + + assert.isTrue(isShuffled); }); }); diff --git a/arkguard/test/ut/utils/NodeUtils.spec.ts b/arkguard/test/ut/utils/NodeUtils.spec.ts deleted file mode 100644 index 6806a7fe78902ee9d9a2220c235341c3814e192c..0000000000000000000000000000000000000000 --- a/arkguard/test/ut/utils/NodeUtils.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import {before, describe} from 'mocha'; -import {assert} from 'chai'; -import {createSourceFile, ScriptTarget, SourceFile} from 'typescript'; -import {NodeUtils} from '../../../src/utils/NodeUtils'; - -describe('test for NodeUtils', function () { - let fileContent; - let sourceFile: SourceFile; - - before('init ast for source file', function () { - fileContent = ` - function sayHello2() { - let student = 'Dudu'; - var _c111 = '3|1|2|4|0'.split('|'), _ddd = [3,1,2,0,4], _0x67e02af2_ = 0; - for (;;) { - switch (_c111[_ddd[_0x67e02af2_++]]) { - case '0': - console.log('when ' + student); - continue; - case '1': - console.log('where ' + student); - continue; - case '2': - console.log('how ' + student); - continue; - case '3': - console.log('what ' + student); - continue; - case '4': - console.log('hello ' + student); - continue; - } - break; - } - } - - for (;;) {} - sayHello2(); - `; - - sourceFile = createSourceFile('demo.js', fileContent, ScriptTarget.ES2015, true); - }); - - describe('test for printNode', function () { - it('functional test', function () { - const printedContent = NodeUtils.printNode(sourceFile, sourceFile); - - const originRemoved = fileContent.replace(/\n/g, '').replace(/\r/g, '').replace(/ /g, ''); - const printedRemoved = printedContent.replace(/\n/g, '').replace(/\r/g, '').replace(/ /g, ''); - - assert.equal(originRemoved, printedRemoved); - }); - }); - - describe('test for method isLoopStatement', function () { - it('functional test', function () { - assert.isTrue(NodeUtils.isLoopStatement(sourceFile.statements[1])); - }); - }); -}); \ No newline at end of file