Skip to content

Commit 77d8dd9

Browse files
committed
refactor: enhance inlineVariableAliases function to improve alias handling and scope management
1 parent e63e4bc commit 77d8dd9

3 files changed

Lines changed: 29 additions & 17 deletions

File tree

packages/deob/src/ast-utils/inline.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ export function inlineFunctionAliases(binding: Binding): { changes: number } {
243243
export function inlineVariableAliases(
244244
binding: Binding,
245245
targetName = binding.identifier.name,
246+
rootTargetName = targetName,
246247
): { changes: number } {
247248
const state = { changes: 0 }
248249
const refs = [...binding.referencePaths]
@@ -267,8 +268,9 @@ export function inlineVariableAliases(
267268
// Avoid infinite loop from `alias = alias;` (caused by dead code injection?)
268269
if (ref.isIdentifier({ name: varBinding.identifier.name })) continue
269270

270-
// Check all further aliases (`var alias2 = alias;`)
271-
state.changes += inlineVariableAliases(varBinding, targetName).changes
271+
// Use current var name when recursing so we only unwind one level (e.g. alias2 -> alias).
272+
// Pass rootTargetName so refs in same scope can be renamed to the root (e.g. decoder).
273+
state.changes += inlineVariableAliases(varBinding, varName.current!, rootTargetName).changes
272274

273275
if (ref.parentPath?.isAssignmentExpression()) {
274276
// Remove `var alias;` when the assignment happens separately
@@ -279,8 +281,11 @@ export function inlineVariableAliases(
279281
ref.parentPath.remove()
280282
}
281283
else {
282-
// Replace `(alias = decoder)(1);` with `decoder(1);`
283-
ref.parentPath.replaceWith(t.identifier(targetName))
284+
// Replace `(alias = decoder)(1);` with `decoder(1);` or `(alias2 = alias)(4)` with `alias(4)`.
285+
// When we're the RHS of an assignment that is a callee, use targetName so the call keeps the alias (e.g. alias(4)).
286+
const isCalleeAssignment = ref.parentKey === 'right' && ref.parentPath?.parentPath?.isCallExpression()
287+
const nameToUse = isCalleeAssignment ? targetName : (binding.identifier.name === targetName ? rootTargetName : targetName)
288+
ref.parentPath.replaceWith(t.identifier(nameToUse))
284289
}
285290
}
286291
else if (ref.parentPath?.isVariableDeclarator()) {
@@ -290,9 +295,15 @@ export function inlineVariableAliases(
290295
state.changes++
291296
}
292297
else {
293-
// Rename the reference
294-
ref.replaceWith(t.identifier(targetName))
295-
state.changes++
298+
// Only rename when the reference is in the same scope as the binding.
299+
// Direct alias of root (binding.name === targetName): use rootTargetName so alias(2) -> decoder(2).
300+
// Deeper alias (e.g. alias2): use targetName so alias2(4) -> alias(4).
301+
// Refs in nested scopes are left as-is so alias(4) stays.
302+
if (ref.scope === binding.scope) {
303+
const nameToUse = binding.identifier.name === targetName ? rootTargetName : targetName
304+
ref.replaceWith(t.identifier(nameToUse))
305+
state.changes++
306+
}
296307
}
297308
}
298309

packages/deob/src/transforms/decode-strings.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ export async function decodeStrings(sandbox: Sandbox, decoders: Decoder[]) {
1616
const map = new Map<string, string>() // 记录解密结果
1717
let failures = 0
1818

19-
for await (const decoder of decoders) {
20-
decoder?.path.scope.getBinding(decoder.name)?.referencePaths.forEach(async (ref) => {
19+
for (const decoder of decoders) {
20+
const refs = decoder?.path.scope.getBinding(decoder.name)?.referencePaths ?? []
21+
for (const ref of refs) {
2122
if (ref?.parentKey === 'callee' && ref.parentPath?.isCallExpression()) {
2223
const callExpression = ref.parentPath
2324
try {
2425
// 如果调用解密函数中有变量参数则不替换
2526
const hasIdentifier = callExpression.node.arguments.some(a => t.isIdentifier(a))
26-
if (hasIdentifier) return
27+
if (hasIdentifier) continue
2728

2829
const call = callExpression.toString()
2930

@@ -38,7 +39,7 @@ export async function decodeStrings(sandbox: Sandbox, decoders: Decoder[]) {
3839
callExpression.addComment('leading', `decode_error: ${(error as any).message}`, true)
3940
}
4041
}
41-
})
42+
}
4243
}
4344

4445
if (failures)

packages/deob/src/transforms/test/decoder.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { findDecoderByArray } from '../find-decoder-by-array'
88
import { findDecoderByCallCount } from '../find-decoder-by-call-count'
99

1010
describe('decoder', async () => {
11-
it('find decoder by call count', () => {
11+
it('find decoder by call count', async () => {
1212
const ast = parse(`
1313
function decoder(a){
1414
return atob(a)
@@ -21,8 +21,8 @@ describe('decoder', async () => {
2121
const sandbox = createNodeSandbox()
2222
const { decoders, setupCode } = findDecoderByCallCount(ast, 2)
2323

24-
evalCode(sandbox, setupCode)
25-
decodeStrings(sandbox, decoders)
24+
await evalCode(sandbox, setupCode)
25+
await decodeStrings(sandbox, decoders)
2626

2727
decoders.forEach(d => d.path.remove())
2828

@@ -32,7 +32,7 @@ describe('decoder', async () => {
3232
"debugger";"
3333
`)
3434
})
35-
it('find decoder by array', () => {
35+
it('find decoder by array', async () => {
3636
const ast = parse(`
3737
var arr = ["hello,world", "debugger"]
3838
function decoder(i){
@@ -50,8 +50,8 @@ describe('decoder', async () => {
5050

5151
const { stringArray, decoders, rotators, setupCode } = findDecoderByArray(ast)
5252

53-
evalCode(sandbox, setupCode)
54-
decodeStrings(sandbox, decoders)
53+
await evalCode(sandbox, setupCode)
54+
await decodeStrings(sandbox, decoders)
5555

5656
stringArray?.path.remove()
5757
decoders.forEach(d => d.path.remove())

0 commit comments

Comments
 (0)