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;

}

}

反混淆順序

最佳的反混淆處理順序如下:

通用執行->訪問反混淆代碼->字符串反混淆->清理執行痕跡

工具運行截圖

相關文章