大殺器Unidbg真正的威力
論2021如何處理 arm vmp?
在apk安全如今的時代,一切安全措施已經可以比肩pe級別的防護,加殼,ollvm混淆,vmp
似乎各大廠商都在不惜用用戶的流暢體驗度,來換取更安全的防護力度,也是2021了,誰還沒有臺曉龍855。CPU性能嚴重溢出,可能就是這些廠商的想法吧,反正不用白不用,這些算力也不是自己的。
但是碰到這么強的加固混淆,逆向人員應該如何是好呢?
有圖有真相 Unidbg的殺手锏 CPU指令級別Trace
ps:Unidbg跟010Editor更配哦!

今天就來帶大家以自身使用的經驗去了解一下Unidbg!
上面那張圖片呢,用的就是小弟魔改的UnidbgTraceCode出來的文件,通過Trace方法可以快速定位某個函數在指令級別的作用,幫助逆向人員更快的分析出想要的算法。
當然Unidbg能做的遠遠不止這些,本文并不是一篇科普文,所以本文假定讀者都是有一定基礎的同學。
callFunction:
如果你仔細觀摩過Uniidbg源碼的話,你會發現所有callJniMethodObject最終都會并入一個叫callFunction的函數。
那么我們在分析So的過程中,發現了一個非JNI函數能不能主動調用呢?答案是必須能。
這種好處的體現在于,逆向人員不必使用Jni函數去分析大量的無用代碼,而是能精確的定位一個小的Func的具體作用以及算法邏輯。

通過該函數我們能得到這個小函數在內部到底做了什么操作,并且能Trace出更精確的指令文件。
public final Number[] callFunction(Emulator emulator, String symbolName, Object... args) { Symbol symbol = findSymbolByName(symbolName, false); if (symbol == null) { throw new IllegalStateException("find symbol failed: " + symbolName); } if (symbol.isUndef()) { throw new IllegalStateException(symbolName + " is NOT defined"); } return symbol.call(emulator, args);}
該函數的第一個參數毋庸置疑是當前的模擬器:
emulator = createARMEmulator();
第二個參數可以是導出函數名,或者是指定地址偏移,后面的參數就是個變長的參數列表,由逆向人員分析得到。
當然如果你是進行so內部函數調用的話,你大概率會填充一個指針,這里在給大家分享一段自己填充char* 類型的函數源碼,別的類型參數同理,不再贅述。
private static void CallVMPFunc(Module module,AndroidEmulator emulator){ try { Symbol malloc = module.findSymbolByName("malloc"); Symbol free = module.findSymbolByName("free"); MemoryBlock block = MemoryAllocBlock.malloc(emulator,malloc,free,0x1000); MemoryBlock namebyte = MemoryAllocBlock.malloc(emulator,malloc,free,0x1000); UnidbgPointer blockpoint = block.getPointer(); UnidbgPointer namepoint = namebyte.getPointer(); String name = "magicillusion"; String data = "hello worid"; namepoint.write(name.getBytes()); blockpoint.write(data.getBytes()); Number[] ret = module.callFunction(emulator,0x13B30+1,namepoint,blockpoint,2); UnidbgPointer ret1 = new UnidbgPointer(emulator,ret[0].intValue(),4); String string = ret1.getString(0); System.out.println("Number => " + (string)); } finally { } }
大殺器內置的HOOK框架
當然Unidbg還內置了多種HOOK框架,今天講一個分析So比較實用的一款HookZz
// 1. 獲取HookZz對象IHookZz hookZz = HookZz.getInstance(emulator); // 加載HookZz,支持inline hook,文檔看https://github.com/jmpews/HookZz// 2. enable hookhookZz.enable_arm_arm64_b_branch(); // 測試enable_arm_arm64_b_branch,可有可無index = 0; hookZz.replace(module.findSymbolByName("lrand48"), new ReplaceCallback() { @Override public void postCall(Emulator emulator, HookContext context) { ((EditableArm32RegisterContext)context).setR0(0x12345678); int ptrace_args0 = context.getIntArg(0); System.out.println("lrand48=" + ptrace_args0); } },true); //aesdecode hookhookZz.wrap((module.base)+0x39634+1, new WrapCallback() { // inline wrap導出函數 UnidbgPointer addr = null; @Override // 4. 方法執行前 public void preCall(Emulator emulator, RegisterContext ctx, HookEntryInfo info) { addr= ctx.getPointerArg(0); UnidbgPointer pointerArg = ctx.getPointerArg(1); UnidbgPointer pointer = pointerArg.getPointer(12); int anInt = pointerArg.getInt(8); byte[] byteArray = pointer.getByteArray(0, anInt); String s =xuzi1(byteArray); System.out.println("aes aesdecode= " + s); } @Override // 5. 方法執行后 public void postCall(Emulator emulator, RegisterContext ctx, HookEntryInfo info) { byte[] aaaa = addr.getPointer(0).getPointer(12).getByteArray(0,0x30); String s =xuzi1(aaaa); System.out.println("aes aesdecode1= " + s); } });
同理,此框架也支持導出函數HOOK以及InlineHOOK 有了這個方法,在你分析一些函數的時候,可以充當Log的效果或者強行改變一些函數的返回值讓你更容易的分析,比如本例中筆者改變了Lrand48的返回值,讓函數每次都強行返回0x12345678,這樣在逆向分析的時候能讓一些不確定性變成可控性。