Skip to content

Commit 3af5edf

Browse files
committed
Add deobfuscator method
1 parent d5402e2 commit 3af5edf

2 files changed

Lines changed: 107 additions & 0 deletions

File tree

index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const Env = require('./lib/env');
1616
const Types = require('./lib/types');
1717
const VM = require('./lib/vm');
1818
const { checkJniResult } = require('./lib/result');
19+
const Deobfuscator = require('./lib/deobfuscator');
1920

2021
const jsizeSize = 4;
2122
const pointerSize = Process.pointerSize;
@@ -566,6 +567,10 @@ class Runtime {
566567

567568
return result;
568569
}
570+
571+
deobfuscator(mapping) {
572+
return new Deobfuscator(mapping);
573+
}
569574
}
570575

571576
function initFactoryFromApplication (factory, app) {

lib/deobfuscator.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
class Deobfuscator {
2+
#mapping;
3+
#reverseMapping;
4+
5+
constructor (mapping) {
6+
if (mapping instanceof Map) {
7+
this.#mapping = mapping;
8+
} else {
9+
const o = Object.entries(mapping).map(e => [e[0], {
10+
realSpecifier: e[1].realSpecifier,
11+
properties: new Map(Object.entries(e[1].properties))
12+
}]);
13+
this.#mapping = new Map(o);
14+
}
15+
16+
this.#reverseMapping = new Map();
17+
for (const [key, value] of this.#mapping.entries()) {
18+
this.#reverseMapping.set(value.realSpecifier, key);
19+
}
20+
}
21+
22+
#getRealSpecifier (deobfuscatedSpecifier) {
23+
const specifierMapping = this.#mapping.get(deobfuscatedSpecifier);
24+
return specifierMapping?.realSpecifier;
25+
}
26+
27+
use (deobfuscatedSpecifier) {
28+
const realSpecifier = this.#getRealSpecifier(deobfuscatedSpecifier) || deobfuscatedSpecifier;
29+
let realUse;
30+
Java.performNow(() => {
31+
realUse = Java.use(realSpecifier);
32+
});
33+
return this.wrap(realUse);
34+
}
35+
36+
choose (deobfuscatedSpecifier, callbacks) {
37+
const realSpecifier = this.#getRealSpecifier(deobfuscatedSpecifier) || deobfuscatedSpecifier;
38+
Java.perform(() => {
39+
Java.choose(realSpecifier, {
40+
onMatch: instance => callbacks.onMatch(this.wrap(instance)),
41+
onComplete: () => callbacks.onComplete()
42+
});
43+
});
44+
}
45+
46+
wrap (wrapper) {
47+
const realSpecifier = wrapper.$className;
48+
const deobfuscatedSpecifier = this.#reverseMapping.get(realSpecifier) || realSpecifier;
49+
const propertiesMapping = this.#mapping.get(deobfuscatedSpecifier)?.properties || new Map();
50+
51+
return new Proxy(wrapper, {
52+
get: (target, prop, receiver) => {
53+
const realProp = propertiesMapping.get(prop) || prop;
54+
const result = Reflect.get(target, realProp, receiver);
55+
56+
if (result) {
57+
if (result.value && result.value.$className) {
58+
return this.#wrapField(result);
59+
}
60+
61+
if (result instanceof Function) {
62+
return this.#wrapMethod(target, result);
63+
}
64+
}
65+
66+
return result;
67+
}
68+
});
69+
}
70+
71+
#wrapField (field) {
72+
return new Proxy(field, {
73+
get: (target, prop, receiver) => {
74+
if (prop === 'value') {
75+
return this.wrap(target.value);
76+
}
77+
return Reflect.get(target, prop, receiver);
78+
}
79+
});
80+
}
81+
82+
#wrapMethod (wrapper, method) {
83+
return new Proxy(method, {
84+
apply: (_, thisArg, argumentsList) => {
85+
const result = method.apply(thisArg, argumentsList);
86+
if (result && result.$className) {
87+
return this.wrap(result);
88+
}
89+
return result;
90+
},
91+
set: (target, prop, newValue, receiver) => {
92+
if (prop === 'implementation') {
93+
return Reflect.set(target, prop, newValue.bind(wrapper), receiver);
94+
}
95+
96+
return Reflect.set(target, prop, newValue, receiver);
97+
}
98+
});
99+
}
100+
}
101+
102+
module.exports = Deobfuscator;

0 commit comments

Comments
 (0)