Skip to content

Commit 4d8f788

Browse files
committed
refactor interpreting constant calls
1 parent 80c5265 commit 4d8f788

File tree

13 files changed

+322
-371
lines changed

13 files changed

+322
-371
lines changed

deobfuscator-api/src/main/java/org/objectweb/asm/tree/AbstractInsnNode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,8 @@ public int getRequiredStackValuesCount() {
673673
// Multi-dimensional array creation
674674
case MULTIANEWARRAY -> ((MultiANewArrayInsnNode) this).dims;
675675

676-
default -> throw new IllegalStateException("Unknown opcode: " + this);
676+
// No values required
677+
default -> 0;
677678
};
678679
}
679680

deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceInterpreter.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
3636
import org.objectweb.asm.tree.LdcInsnNode;
3737
import org.objectweb.asm.tree.MethodInsnNode;
38+
import uwu.narumi.deobfuscator.api.asm.MethodlessInsnContext;
3839
import uwu.narumi.deobfuscator.api.helper.AsmMathHelper;
40+
import uwu.narumi.deobfuscator.api.helper.SimpleInterpreter;
3941

4042
import java.util.HashSet;
4143
import java.util.List;
@@ -238,6 +240,21 @@ public OriginalSourceValue naryOperation(
238240
} else {
239241
size = Type.getReturnType(((MethodInsnNode) insn).desc).getSize();
240242
}
243+
244+
// Narumii start - Predict constant
245+
MethodlessInsnContext insnContext = new MethodlessInsnContext(insn, null);
246+
247+
// Transform method calls on literals
248+
for (SimpleInterpreter.MethodInterpreter methodInterpreter : SimpleInterpreter.METHOD_INTERPRETERS) {
249+
if (methodInterpreter.match().matches(insnContext)) {
250+
OriginalSourceValue.ConstantValue constantValue = methodInterpreter.methodComputation().computeConstant(insn, values);
251+
if (constantValue != null) {
252+
return new OriginalSourceValue(size, insn, null, constantValue);
253+
}
254+
}
255+
}
256+
// Narumii end
257+
241258
return new OriginalSourceValue(size, insn);
242259
}
243260

@@ -249,7 +266,7 @@ public void returnOperation(
249266

250267
@Override
251268
public OriginalSourceValue merge(final OriginalSourceValue value1, final OriginalSourceValue value2) {
252-
if (value1.size != value2.size || !containsAll(value1.insns, value2.insns) || !Objects.equals(value1.copiedFrom, value2.copiedFrom)) {
269+
if (value1.size != value2.size || !Objects.equals(value1.getConstantValue(), value2.getConstantValue()) || !containsAll(value1.insns, value2.insns) || !Objects.equals(value1.copiedFrom, value2.copiedFrom)) {
253270
Set<AbstractInsnNode> setUnion;
254271
if (value1.insns instanceof SmallSet && value2.insns instanceof SmallSet) {
255272
// Use optimized merging method

deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,12 @@ public boolean equals(Object o) {
203203
if (o == null || getClass() != o.getClass()) return false;
204204
if (!super.equals(o)) return false;
205205
OriginalSourceValue that = (OriginalSourceValue) o;
206-
return Objects.equals(copiedFrom, that.copiedFrom);
206+
return Objects.equals(constantValue, that.constantValue) && Objects.equals(copiedFrom, that.copiedFrom);
207207
}
208208

209209
@Override
210210
public int hashCode() {
211-
return Objects.hash(super.hashCode(), copiedFrom);
211+
return Objects.hash(super.hashCode(), constantValue, copiedFrom);
212212
}
213213

214214
/**
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package uwu.narumi.deobfuscator.api.asm;
2+
3+
import org.objectweb.asm.tree.AbstractInsnNode;
4+
import org.objectweb.asm.tree.MethodNode;
5+
import org.objectweb.asm.tree.analysis.Frame;
6+
import org.objectweb.asm.tree.analysis.OriginalSourceValue;
7+
8+
public class MethodlessInsnContext extends InsnContext {
9+
private final Frame<OriginalSourceValue> frame;
10+
11+
public MethodlessInsnContext(AbstractInsnNode insn, Frame<OriginalSourceValue> frame) {
12+
super(insn, null);
13+
this.frame = frame;
14+
}
15+
16+
@Override
17+
public Frame<OriginalSourceValue> frame() {
18+
return this.frame;
19+
}
20+
21+
@Override
22+
public MethodNode methodNode() {
23+
throw new UnsupportedOperationException();
24+
}
25+
26+
@Override
27+
public InsnContext of(AbstractInsnNode insn) {
28+
throw new UnsupportedOperationException();
29+
}
30+
31+
@Override
32+
public MethodContext methodContext() {
33+
throw new UnsupportedOperationException();
34+
}
35+
}

deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmMathHelper.java

Lines changed: 0 additions & 247 deletions
Original file line numberDiff line numberDiff line change
@@ -116,253 +116,6 @@ public final class AsmMathHelper {
116116
Map.entry(D2L, Number::longValue)
117117
);
118118

119-
public static final Match STRING_LENGTH =
120-
MethodMatch.invokeVirtual()
121-
.owner("java/lang/String")
122-
.name("length")
123-
.desc("()I")
124-
.defineTransformation(context -> {
125-
// Get value from stack
126-
OriginalSourceValue sourceValue = context.frame().getStack(context.frame().getStackSize() - 1);
127-
OriginalSourceValue originalSourceValue = sourceValue.originalSource;
128-
if (!originalSourceValue.isOneWayProduced()) return false;
129-
130-
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
131-
if (!originalInsn.isString()) return false;
132-
133-
context.placePops();
134-
context.methodNode().instructions.set(
135-
context.insn(),
136-
AsmHelper.numberInsn(originalInsn.asString().length())
137-
);
138-
return true;
139-
});
140-
141-
public static final Match STRING_HASHCODE =
142-
MethodMatch.invokeVirtual()
143-
.owner("java/lang/String")
144-
.name("hashCode")
145-
.desc("()I")
146-
.defineTransformation(context -> {
147-
// Get value from stack
148-
OriginalSourceValue sourceValue = context.frame().getStack(context.frame().getStackSize() - 1);
149-
OriginalSourceValue originalSourceValue = sourceValue.originalSource;
150-
if (!originalSourceValue.isOneWayProduced()) return false;
151-
152-
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
153-
if (!originalInsn.isString()) return false;
154-
155-
context.placePops();
156-
context.methodNode().instructions.set(
157-
context.insn(),
158-
AsmHelper.numberInsn(originalInsn.asString().hashCode())
159-
);
160-
return true;
161-
});
162-
163-
public static final Match STRING_TO_INTEGER =
164-
MethodMatch.invokeStatic()
165-
.owner("java/lang/Integer")
166-
.name("parseInt", "valueOf")
167-
.desc("(Ljava/lang/String;)I")
168-
.defineTransformation(context -> {
169-
// Get value from stack
170-
OriginalSourceValue sourceValue = context.frame().getStack(context.frame().getStackSize() - 1);
171-
OriginalSourceValue originalSourceValue = sourceValue.originalSource;
172-
if (!originalSourceValue.isOneWayProduced()) return false;
173-
174-
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
175-
// Integer#parseInt(String)
176-
if (!originalInsn.isString()) return false;
177-
178-
context.placePops();
179-
context.methodNode().instructions.set(
180-
context.insn(),
181-
AsmHelper.numberInsn(Integer.parseInt(originalInsn.asString()))
182-
);
183-
return true;
184-
});
185-
186-
public static final Match STRING_TO_INTEGER_RADIX =
187-
MethodMatch.invokeStatic()
188-
.owner("java/lang/Integer")
189-
.name("parseInt", "valueOf")
190-
.desc("(Ljava/lang/String;I)I")
191-
.defineTransformation(context -> {
192-
// Get values from stack
193-
OriginalSourceValue firstValue = context.frame().getStack(context.frame().getStackSize() - 2);
194-
OriginalSourceValue originalFirstValue = firstValue.originalSource;
195-
OriginalSourceValue secondValue = context.frame().getStack(context.frame().getStackSize() - 1);
196-
OriginalSourceValue originalSecondValue = secondValue.originalSource;
197-
if (!originalFirstValue.isOneWayProduced() || !originalSecondValue.isOneWayProduced()) return false;
198-
199-
AbstractInsnNode originalFirstInsn = originalFirstValue.getProducer();
200-
AbstractInsnNode originalSecondInsn = originalSecondValue.getProducer();
201-
202-
// Integer#parseInt(String, int)
203-
if (!originalFirstInsn.isString() || !originalSecondInsn.isInteger()) return false;
204-
205-
context.placePops();
206-
context.methodNode().instructions.set(
207-
context.insn(),
208-
AsmHelper.numberInsn(
209-
Integer.parseInt(originalFirstInsn.asString(), originalSecondInsn.asInteger())
210-
)
211-
);
212-
return true;
213-
});
214-
215-
public static final Match INTEGER_REVERSE =
216-
MethodMatch.invokeStatic()
217-
.owner("java/lang/Integer")
218-
.name("reverse")
219-
.desc("(I)I")
220-
.defineTransformation(context -> {
221-
// Get value from stack
222-
OriginalSourceValue sourceValue = context.frame().getStack(context.frame().getStackSize() - 1);
223-
OriginalSourceValue originalSourceValue = sourceValue.originalSource;
224-
if (!originalSourceValue.isOneWayProduced()) return false;
225-
226-
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
227-
// Integer#reverse(int)
228-
if (!originalInsn.isInteger()) return false;
229-
230-
context.placePops();
231-
context.methodNode().instructions.set(
232-
context.insn(),
233-
AsmHelper.numberInsn(Integer.reverse(originalInsn.asInteger()))
234-
);
235-
return true;
236-
});
237-
238-
public static final Match LONG_REVERSE =
239-
MethodMatch.invokeStatic()
240-
.owner("java/lang/Long")
241-
.name("reverse")
242-
.desc("(J)J")
243-
.defineTransformation(context -> {
244-
// Get value from stack
245-
OriginalSourceValue sourceValue = context.frame().getStack(context.frame().getStackSize() - 1);
246-
OriginalSourceValue originalSourceValue = sourceValue.originalSource;
247-
if (!originalSourceValue.isOneWayProduced()) return false;
248-
249-
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
250-
// Long#reverse(long)
251-
if (!originalInsn.isLong()) return false;
252-
253-
context.placePops();
254-
context.methodNode().instructions.set(
255-
context.insn(),
256-
AsmHelper.numberInsn(Long.reverse(originalInsn.asLong()))
257-
);
258-
return true;
259-
});
260-
261-
public static final Match FLOAT_TO_BITS =
262-
MethodMatch.invokeStatic()
263-
.owner("java/lang/Float")
264-
.name("floatToIntBits")
265-
.desc("(F)I")
266-
.defineTransformation(context -> {
267-
// Get value from stack
268-
OriginalSourceValue sourceValue = context.frame().getStack(context.frame().getStackSize() - 1);
269-
OriginalSourceValue originalSourceValue = sourceValue.originalSource;
270-
if (!originalSourceValue.isOneWayProduced()) return false;
271-
272-
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
273-
// Float#floatToIntBits(float)
274-
if (!originalInsn.isFloat()) return false;
275-
276-
context.placePops();
277-
context.methodNode().instructions.set(
278-
context.insn(),
279-
AsmHelper.numberInsn(Float.floatToIntBits(originalInsn.asFloat()))
280-
);
281-
return true;
282-
});
283-
284-
public static final Match BITS_TO_FLOAT =
285-
MethodMatch.invokeStatic()
286-
.owner("java/lang/Float")
287-
.name("intBitsToFloat")
288-
.desc("(I)F")
289-
.defineTransformation(context -> {
290-
// Get value from stack
291-
OriginalSourceValue sourceValue = context.frame().getStack(context.frame().getStackSize() - 1);
292-
OriginalSourceValue originalSourceValue = sourceValue.originalSource;
293-
if (!originalSourceValue.isOneWayProduced()) return false;
294-
295-
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
296-
// Float#intBitsToFloat(int)
297-
if (!originalInsn.isInteger()) return false;
298-
299-
context.placePops();
300-
context.methodNode().instructions.set(
301-
context.insn(),
302-
AsmHelper.numberInsn(Float.intBitsToFloat(originalInsn.asInteger()))
303-
);
304-
return true;
305-
});
306-
307-
public static final Match DOUBLE_TO_BITS =
308-
MethodMatch.invokeStatic()
309-
.owner("java/lang/Double")
310-
.name("doubleToLongBits")
311-
.desc("(D)J")
312-
.defineTransformation(context -> {
313-
// Get value from stack
314-
OriginalSourceValue sourceValue = context.frame().getStack(context.frame().getStackSize() - 1);
315-
OriginalSourceValue originalSourceValue = sourceValue.originalSource;
316-
if (!originalSourceValue.isOneWayProduced()) return false;
317-
318-
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
319-
// Double#doubleToLongBits(double)
320-
if (!originalInsn.isDouble()) return false;
321-
322-
context.placePops();
323-
context.methodNode().instructions.set(
324-
context.insn(),
325-
AsmHelper.numberInsn(Double.doubleToLongBits(originalInsn.asDouble()))
326-
);
327-
return true;
328-
});
329-
330-
public static final Match BITS_TO_DOUBLE =
331-
MethodMatch.invokeStatic()
332-
.owner("java/lang/Double")
333-
.name("longBitsToDouble")
334-
.desc("(J)D")
335-
.defineTransformation(context -> {
336-
// Get value from stack
337-
OriginalSourceValue sourceValue = context.frame().getStack(context.frame().getStackSize() - 1);
338-
OriginalSourceValue originalSourceValue = sourceValue.originalSource;
339-
if (!originalSourceValue.isOneWayProduced()) return false;
340-
341-
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
342-
// Double#longBitsToDouble(long)
343-
if (!originalInsn.isLong()) return false;
344-
345-
context.placePops();
346-
context.methodNode().instructions.set(
347-
context.insn(),
348-
AsmHelper.numberInsn(Double.longBitsToDouble(originalInsn.asLong()))
349-
);
350-
return true;
351-
});
352-
353-
public static final List<Match> METHOD_CALLS_ON_LITERALS = List.of(
354-
STRING_LENGTH,
355-
STRING_HASHCODE,
356-
STRING_TO_INTEGER,
357-
STRING_TO_INTEGER_RADIX,
358-
INTEGER_REVERSE,
359-
LONG_REVERSE,
360-
FLOAT_TO_BITS,
361-
BITS_TO_FLOAT,
362-
DOUBLE_TO_BITS,
363-
BITS_TO_DOUBLE
364-
);
365-
366119
private AsmMathHelper() {
367120
throw new IllegalArgumentException();
368121
}

0 commit comments

Comments
 (0)