diff --git a/arkguard/package.json b/arkguard/package.json index ca3d370bebd7ac7d7838cec2ab8855f77acc5493..a457ac038b2b847a06f6ded64ddb643d8eb6dc8a 100644 --- a/arkguard/package.json +++ b/arkguard/package.json @@ -1,6 +1,6 @@ { "name": "arkguard", - "version": "1.0.0", + "version": "1.0.5", "description": "An obfuscator tools for open harmony apps.", "bin": { "arkguard": "bin/secharmony" diff --git a/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts b/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts index 412928c20a72f7ce6c44618ab1b818d8138972ab..9de34e8ab96cd78cd0e91b78c57109494d618a3f 100644 --- a/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts +++ b/arkguard/src/transformers/rename/RenameIdentifierTransformer.ts @@ -41,7 +41,8 @@ import { isGlobalScope, isEnumScope, isInterfaceScope, - isObjectLiteralScope + isObjectLiteralScope, + mangledIdentifierNames } from '../../utils/ScopeAnalyzer'; import type { @@ -115,6 +116,9 @@ namespace secharmony { if (!isSourceFile(node)) { return node; } + if (mangledIdentifierNames.size > 0) { + mangledIdentifierNames.clear(); + } const shadowSourceAst: SourceFile = TypeUtils.createNewSourceFile(node); checker = TypeUtils.createChecker(shadowSourceAst); @@ -153,10 +157,6 @@ namespace secharmony { function renameNamesInScope(scope: Scope): void { if (scope.parent) { - scope.parent.mangledNames.forEach((value) => { - scope.mangledNames.add(value); - }); - scope.parent.importNames.forEach((value) => { scope.importNames.add(value); }); @@ -185,7 +185,7 @@ namespace secharmony { let mangled: string = original; // No allow to rename reserved names. if (reservedNames.includes(original) || scope.exportNames.has(def.name) || isSkippedGlobal(openTopLevel, scope)) { - scope.mangledNames.add(mangled); + mangledIdentifierNames.add(mangled); mangledSymbolNames.set(def, mangled); return; } @@ -206,7 +206,7 @@ namespace secharmony { // add new names to name cache nameCache.set(path, mangled); - scope.mangledNames.add(mangled); + mangledIdentifierNames.add(mangled); mangledSymbolNames.set(def, mangled); localCache.set(original, mangled); }); @@ -254,7 +254,7 @@ namespace secharmony { } // the anme has already been generated in the current scope - if (scope.mangledNames.has(mangled)) { + if (mangledIdentifierNames.has(mangled)) { mangled = ''; } } while (mangled === ''); @@ -337,7 +337,7 @@ namespace secharmony { const sym: Symbol | undefined = checker.getSymbolAtLocation(targetNode); if (!sym) { - scope.mangledNames.add((targetNode as Identifier).escapedText.toString()); + mangledIdentifierNames.add((targetNode as Identifier).escapedText.toString()); } }; diff --git a/arkguard/src/utils/NodeUtils.ts b/arkguard/src/utils/NodeUtils.ts index 11d24da2002733fdd1c2c39da1352c91ffc3ed47..022ceb262cc374ab78bc9004e001257b6008db69 100644 --- a/arkguard/src/utils/NodeUtils.ts +++ b/arkguard/src/utils/NodeUtils.ts @@ -34,6 +34,7 @@ import { isSetAccessor, isVariableDeclaration } from 'typescript'; +import { isParameterPropertyModifier } from './OhsUtil'; export class NodeUtils { public static isPropertyDeclarationNode(node: Node): boolean { @@ -123,6 +124,11 @@ export class NodeUtils { return false; } + const modifiers = node.parent.modifiers; + if (!modifiers || modifiers.length === 0 || !modifiers.find(modifier => isParameterPropertyModifier(modifier))) { + return false; + } + return node.parent.parent && isConstructorDeclaration(node.parent.parent); } diff --git a/arkguard/src/utils/OhsUtil.ts b/arkguard/src/utils/OhsUtil.ts index 2588197cf1e76e08057472a0e95051dd11b523ab..f729f7f0263797735e44a87661c62df20a91b406 100644 --- a/arkguard/src/utils/OhsUtil.ts +++ b/arkguard/src/utils/OhsUtil.ts @@ -249,7 +249,7 @@ export function getInterfaceProperties(interfaceNode: InterfaceDeclaration, prop }); } -function isParameterPropertyModifier(modifier: Modifier): boolean { +export function isParameterPropertyModifier(modifier: Modifier): boolean { if (modifier.kind === SyntaxKind.PublicKeyword || modifier.kind === SyntaxKind.PrivateKeyword || modifier.kind === SyntaxKind.ProtectedKeyword || diff --git a/arkguard/src/utils/ScopeAnalyzer.ts b/arkguard/src/utils/ScopeAnalyzer.ts index abb71ec030c5887759faf3f644d82e7b58105a3b..3fb1d7722d77cd46cfbe74d40f23f8546540e70d 100644 --- a/arkguard/src/utils/ScopeAnalyzer.ts +++ b/arkguard/src/utils/ScopeAnalyzer.ts @@ -40,10 +40,12 @@ import type { ImportSpecifier, InterfaceDeclaration, LabeledStatement, + Modifier, ModuleDeclaration, Node, ObjectBindingPattern, ObjectLiteralExpression, + ParameterDeclaration, SourceFile, Symbol, SymbolTable, @@ -53,7 +55,7 @@ import type { } from 'typescript'; import {NodeUtils} from './NodeUtils'; -import {isViewPUBasedClass} from './OhsUtil'; +import {isParameterPropertyModifier, isViewPUBasedClass} from './OhsUtil'; /** * kind of a scope @@ -103,6 +105,7 @@ namespace secharmony { return scope.kind === ScopeKind.OBJECT_LITERAL; } + export const mangledIdentifierNames: Set = new Set(); /** * Structure of a scope */ @@ -151,8 +154,6 @@ namespace secharmony { exportNames?: Set; - mangledNames?: Set; - /** * add a sub scope to current scope * @@ -228,7 +229,6 @@ namespace secharmony { 'loc': loc, 'importNames': importNames, 'exportNames': exportNames, - 'mangledNames': mangledNames, addChild, addDefinition, addLabel, @@ -631,17 +631,19 @@ namespace secharmony { return; } - const visitParam = (param: Node): void => { - if (isIdentifier(param)) { - current.defs.forEach((def) => { - if (def.name === param.text) { - current.defs.delete(def); - current.mangledNames.add(def.name); - } - }); + const visitParam = (param: ParameterDeclaration): void => { + const modifiers = param.modifiers; + if (modifiers && modifiers.length > 0) { + const hasParameterPropertyModifier: boolean = modifiers.find(modifier => isParameterPropertyModifier(modifier)) !== undefined; + if (isIdentifier(param.name) && hasParameterPropertyModifier) { + current.defs.forEach((def) => { + if (def.name === param.name.getText()) { + current.defs.delete(def); + mangledIdentifierNames.add(def.name); + } + }); + } } - - forEachChild(param, visitParam); }; node.parameters.forEach((param) => { diff --git a/arkguard/test/grammar/identifier_validation/constructor_property.ts b/arkguard/test/grammar/identifier_validation/constructor_property.ts new file mode 100644 index 0000000000000000000000000000000000000000..47569a1cc0437d63590eaf5e6e9f5c26fdbaa015 --- /dev/null +++ b/arkguard/test/grammar/identifier_validation/constructor_property.ts @@ -0,0 +1,49 @@ +/* + * 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. + */ + +namespace ts { + let friendA: { getX(o: A): number, setX(o: A, v: number): void }; + + class A { + x: number; + + constructor (v: number) { + this.x = v; + } + + getX () { + return this.x; + } + + obj() { + friendA = { + getX(obj) { return obj.x }, + setX(obj, value) { obj.x = value } + }; + } + }; + + class B { + constructor(public a: A, private x01: number = 1, protected x02: string = '', readonly x03: number = 2) { + const x = friendA.getX(a); // ok + friendA.setX(a, x + 1); // ok + } + }; + + const a = new A(41); + a.obj(); + const b = new B(a); + a.getX(); +} \ No newline at end of file diff --git a/arkguard/test/grammar/identifier_validation/constructor_var_property.ts b/arkguard/test/grammar/identifier_validation/constructor_var_property.ts new file mode 100644 index 0000000000000000000000000000000000000000..7f560ea8d40de66b5380e067b42671ddac03dcfe --- /dev/null +++ b/arkguard/test/grammar/identifier_validation/constructor_var_property.ts @@ -0,0 +1,38 @@ +/* + * 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 from 'assert'; +namespace ts { + class X { + n: string = ""; + constructor(s:string) { + this.n = s; + } + method(){ + return (this.n); + } + } + let name1 = new X("global"); + + class A { + name0:string = ''; + constructor(name2:string, public name3: X) { + name3.method(); + } + } + let a = new A('aa',new X("param")); + assert(a.name3.n === 'param', 'success'); + assert(name1.n === 'global', 'success'); +} \ No newline at end of file diff --git a/arkguard/test/grammar/identifier_validation/constructor_variables.ts b/arkguard/test/grammar/identifier_validation/constructor_variables.ts new file mode 100644 index 0000000000000000000000000000000000000000..86493906aea03199ab73ee913e329fc3da3f3d74 --- /dev/null +++ b/arkguard/test/grammar/identifier_validation/constructor_variables.ts @@ -0,0 +1,48 @@ +/* + * 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. + */ + +namespace ts { + let friendA: { getX(o: A): number, setX(o: A, v: number): void }; + class A { + x: number; + + constructor (v: number) { + this.x = v; + } + + getX () { + return this.x; + } + + obj() { + friendA = { + getX(obj) { return obj.x }, + setX(obj, value) { obj.x = value } + }; + } + }; + + class B { + constructor(a: A) { + const x = friendA.getX(a); // ok + friendA.setX(a, x + 1); // ok + } + }; + + const a = new A(41); + a.obj(); + const b = new B(a); + a.getX(); +} \ No newline at end of file