Threadtear:一款多功能Java代碼反混淆工具套件
Threadtear
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; } }
反混淆順序
最佳的反混淆處理順序如下:
通用執行->訪問反混淆代碼->字符串反混淆->清理執行痕跡
工具運行截圖