During the development process, we have encountered some three-party libraries that are not open source. Sometimes we need to refer to some of the codes, and we need to decompile them to view them. It is also sometimes encountered that the code has been obfuscated. This article mainly introduces how to use Threadtear to deobfuscate the obfuscated jar package.
Threadtear Git address: https://github.com/GraxCode/threadtear
compile executable jar
There are two ways:
1. Directly download the latest version released by Github. (I downloaded the latest version 3.0.1 and found that it cannot run normally and has abnormalities)
2. Download the Git project code and import the project with Android Studio. And execute the following two commands, and then an executable jar file will be obtained in the builds/libs of the gui module: threadtear-gui-3.0.0-all.jar
.
gradle build
gradle fatJar
tool use
Run the following command in the directory of the executable jar file just generated (you can also copy the jar file just generated to a new directory) to open the Threadtear tool:
java -noverify -jar threadtear-gui-3.0.0-all.jar
The tool interface is as shown in the figure below:
There is a Look and feel settings under the help menu to set the interface style, mainly to set the text size, the default size is a bit small. . .
Next, we first set the tasks to be executed, click the Add button, as shown in the figure below:
Next, we add the tasks shown in the figure below to the executable list in order:
Then, we will need to decompile and deobfuscate the jar directly Drag it to the Class list area on the right.
Here I haven't found an execution list combination that completely restores all the obfuscated variables of each file to their original names. The door-to-door execution task is only to parse out each class name. The variable name is still under study, please update in time if there is any progress, sorry~
Custom Execution
As shown in the figure above, after downloading the project source code, create a new custom directory in the core module, create a custom Execution, and add the custom Execution to ExecutionLink. Then repack. In this way, we can see the custom Execution we just created in the ExecutionList of the executable jar.
public class MyExecution extends Execution implements IConstantReferenceHandler {
public MyExecution() {
super(ExecutionCategory.GENERIC, "My execution", "Performs stack analysis and replaces code.");
}
@Override
public boolean execute(Map<String, Clazz> classes, boolean verbose) {
classes.values().stream().map(c -> c.node).forEach(this::analyzeAndRewrite);
return true;
}
public void analyzeAndRewrite(ClassNode cn) {
cn.methods.forEach(m -> {
// this analyzer keeps known stack values, e.g. can be useful for jump prediction
Analyzer<ConstantValue> a = new Analyzer<ConstantValue>(new ConstantTracker(this,
Access.isStatic(m.access), m.maxLocals, m.desc, new Object[0]));
try {
a.analyze(cn.name, m);
} catch (AnalyzerException e) {
logger.error("Failed stack analysis in " + cn.name + "." + m.name + ":" + e.getMessage());
return;
}
Frame<ConstantValue>[] frames = a.getFrames();
InsnList rewrittenCode = new InsnList();
Map<LabelNode, LabelNode> labels = Instructions.cloneLabels(m.instructions);
// rewrite method instructions
for (int i = 0; i < m.instructions.size(); i++) {
AbstractInsnNode ain = m.instructions.get(i);
Frame<ConstantValue> frame = frames[i];
// replace / modify instructions, etc...
if (frame.getStackSize() > 0) {
ConstantValue top = frame.getStack(frame.getStackSize() - 1);
if (top.isKnown() && top.isInteger()) {
int knownTopStackValue = top.getAsInteger();
// use the known stack to remove jumps, simplify code, etc...
// if(...) { rewrittenCode.add(...); }
continue;
}
}
rewrittenCode.add(ain.clone(labels));
}
// update instructions and fix try catch blocks, local variables, etc...
Instructions.updateInstructions(m, labels, rewrittenCode);
});
}
/**
* Use this method to predict stack values if fields are loaded
*/
@Override
public Object getFieldValueOrNull(BasicValue v, String owner, String name, String desc) {
return null;
}
/**
* Use this method to predict stack values if methods are invoked on known objects
*/
@Override
public Object getMethodReturnOrNull(BasicValue v, String owner, String name, String desc, List<?
extends ConstantValue> values) {
if (name.equals("toCharArray") && owner.equals("java/lang/String")) {
if (!values.get(0).isKnown()) {
// invocation target is not known, we can't compute the return
return null;
}
return ((String) values.get(0).getValue()).toCharArray();
}
return null;
}
}