unidbg 是一個基於 unicorn 的逆向工具,能夠黑盒調用安卓和 iOS 中的 so 文件。unidbg 是一個標準的 java 項目。java
因爲如今的大多數 app 把簽名算法已經放到了 so 文件中,因此要想破解簽名算法,必須可以破解 so 文件。可是咱們知道,C++ 的逆向遠比 Java 的逆向要可貴多了,因此好多時候是無法破解的,那麼這個時候還能夠採用 hook 的方法,直接讀取程序中算出來的簽名,可是這樣的話,須要實際運行這個應用,須要模擬器或者真機,效率又不是很高。linux
unidbg 就是一個很巧妙地解決方案,他不須要直接運行 app,也無需逆向 so 文件,而是經過在 app 中找到對應的 JNI 接口,而後用 unicorn 引擎直接執行這個 so 文件,因此效率也比較高。git
案例來自JXU2QkQyYXBwJTIwdjQuMTYuMA==
對於該app而言,是很是適合入門的一個app,未加固、算法簡單、很容易找到so的jni。
先去凱神的github上下載https://github.com/zhkl0228/unidbg
下載完畢用idea打開,等待maven下載完畢。我這裏已經建立好du的文件。github
上個代碼看着比較方便,代碼中有不少註釋web
public class du extends AbstractJni { //ARM模擬器 private final ARMEmulator emulator; //vm private final VM vm; //載入的模塊 private final Module module; private final DvmClass TTEncryptUtils; //初始化 public du() throws IOException { //建立app進程,這裏其實能夠不用寫的,我這裏是隨便寫的,使用app自己的進程就能夠繞過進程檢測 emulator = new AndroidARMEmulator("com.du.du"); Memory memory = emulator.getMemory(); //做者支持19和23兩個sdk memory.setLibraryResolver(new AndroidResolver(23)); memory.setCallInitFunction(); //建立DalvikVM,利用apk自己,能夠爲null //若是用apk文件加載so的話,會自動處理簽名方面的jni,具體可看AbstractJni,利用apk加載的好處, // vm = emulator.createDalvikVM(new File("src/test/resources/du/du4160.apk")); 我這裏沒有用到apk,主要是沒有檢測其餘因素。 vm = emulator.createDalvikVM(null); //加載so,使用armv8-64速度會快不少,這裏是so的文件路徑,其實也能夠利用apk自身的。 DalvikModule dm = vm.loadLibrary(new File("src/test/resources/du/libJNIEncrypt.so"), false); //調用jni dm.callJNI_OnLoad(emulator); module = dm.getModule(); //加載so的那個類 TTEncryptUtils = vm.resolveClass("com/duapp/aesjni/AESEncrypt"); } //關閉模擬器 private void destroy() throws IOException { emulator.close(); System.out.println("destroy"); } public static void main(String[] args) throws IOException { du t = new du(); t.encodeByte(); t.destroy(); } private String encodeByte() { //調試 // 這裏還支持gdb調試, //emulator.attach(DebuggerType.GDB_SERVER); //附加調試器 // emulator.attach(DebuggerType.SIMPLE); // emulator.traceCode(); //這裏是打斷點,原地址0x00005028->新地址0x40005028 新地址須要改爲0x4 // emulator.attach().addBreakPoint(null, 0x40001188);//encode地址 // emulator.attach().addBreakPoint(null, 0x40000D10); Number ret = TTEncryptUtils.callStaticJniMethod(emulator, "getByteValues()Ljava/lang/String;"); long hash = ret.intValue() & 0xffffffffL; StringObject st1 = vm.getObject(hash); //*這裏要處理下字符串 String byteString = st1.getValue(); StringBuilder builder = new StringBuilder(byteString.length()); for (int i = 0; i < byteString.length(); i++) { if (byteString.charAt(i) == '0') { builder.append('1'); } else { builder.append('0'); } } //獲取encodeByte地址 ret = TTEncryptUtils.callStaticJniMethod(emulator, "encodeByte(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", //傳參,這裏須要兩個字符串,因此就傳入兩個參數 vm.addLocalObject(new StringObject(vm, "要加密的值")), vm.addLocalObject(new StringObject(vm, builder.toString()))); //ret 返回的是地址, hash = ret.intValue() & 0xffffffffL; //得到其值 StringObject str = vm.getObject(hash); System.out.println(str.getValue()); return str.getValue(); } }
上邊代碼有jni的類是哪個須要知道,就是下面這個類,這個實際上是和加載so有關係的。算法
TTEncryptUtils = vm.resolveClass("com/*/aesjni/AESEncrypt");
咱們須要逆向app,這裏不細說如何在app中尋找加載so的類。以下圖,encodeByte是該app調用native層加密的入口,loadLibrary是java加載so的方法,這個類就是上述代碼中填寫的。spring
而後再看"encodeByte(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"
這裏,這是smali寫法,不補基礎,後面跟上須要傳的參數,getByteValues
這個方法是毒獲取的一個01字符串,而且在java層進行了處理,而後再傳進encodeByte
裏面,encodeByte
這個方法最後獲取的其實並非最終須要的,須要md5纔是最後的newSign。能夠驗證一下下。springboot
測試結果經過。app
啓動java文件時候注意這個改爲本身的平臺!!!框架
VM options: -Djava.library.path=prebuilt/os -Djna.library.path=prebuilt/os Where os may: linux64, win32, win64, osx64
最後這個文件放在https://github.com/zhaoboy9692/dailyanalysis喜歡的能夠star,謝謝。