Threadtear是一款针对Java代码的多功能反混淆工具,该工具即将添加针对Android应用程序的支持。在该工具的帮助下,广大研究人员无需过多担心代码混淆方面的问题,因为Threadtear可以为你的代码分析过程添砖加瓦。即使是ZKM和Stringer之类的混淆工具,对于Threadtear来说也不在话下。为了方便进行代码调试,Threadtear还集成了很多其他的功能,并且还提供了代码行标注以及其他的代码栈追踪功能。值得一提的是,Threadtear还支持逆向分析功能。
一次“任务执行”指的是对所有已加载的类文件进行执行和修改操作,执行任务有很多种类型,从字节码清理到字符串反混淆,但所有的任务都需要确保文件以正确的顺序加载和执行。一切准备就绪之后,点击“Run”按钮即可按顺序对目标文件进行操作。
Threadtear会使用自己的SecurityManager类来尽可能地帮助研究人员免受恶意调用(任意代码执行)的影响,但无法100%保证安全。尤其是在处理类似ZKM或Stringer这样的混淆目标时,反射是经常会出现的。
首先,运行下列命令,然后在builds/libs中会创建一个可运行的jar文件。
gradle build gradle fatJar
如果你不想下载项目源码的话,你还可以直接使用该项目Release页面提供的最新版本:【 点我获取 】。
我们可以直接通过扩展me.nov.threadtear.execution.Execution方法来创建自己的执行任务:
public class MyExecution extends Execution { public MyExecution() { super(ExecutionCategory.CLEANING /* category */, "My execution" /* name */, "Executes something" /* description, can use html */); } /** * This method is invoked when the user clicks on the Run button * @return true if success, false if failure */ @Override public boolean execute(Map<String, Clazz> classes, boolean verbose) { classes.values().stream().map(c -> c.node).forEach(c -> { //transform the classes here using the tree-API of ASM }); return false; } }
在运行时加载 ClassNodes
类,可以直接使用 me.nov.threadtear.asm.vm.VM
类并实现 me.nov.threadtear.asm.vm.IVMReferenceHandler
方法:
public class MyExecution extends Execution implements IVMReferenceHandler { public MyExecution() { super(ExecutionCategory.GENERIC, "My execution", "Loads ClassNodes at runtime"); } @Override public boolean execute(Map<String, Clazz> classes, boolean verbose) { classes.values().stream().map(c -> c.node).forEach(c -> { VM vm = VM.constructVM(this); //transform bytecode to java.lang.Class Class<?> loadedClass = vm.loadClass(c.name.replace('/', '.'), true); //do stuff with your class here loadedClass.getMethods[0].invoke(...); return true; }); } /** * Will get invoked by VM, when VM.loadClass is called */ @Override public ClassNode tryClassLoad(String name) { //try to find the class to be loaded in open jar archive return classes.containsKey(name) ? classes.get(name).node : null; } }
通过使用 me.nov.threadtear.analysis.stack.ConstantTracker
方法,你可以分析目标代码中的方法并追踪非变量栈值:
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.severe("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.getInteger(); // 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; } }
最佳的反混淆处理顺序如下:
通用执行->访问反混淆代码->字符串反混淆->清理执行痕迹