某右APP sign-v2算法分析

某右APP sign-v2算法分析

1、前言

閒來無事,打算鞏固一下以前學的知識,偶然間發現某右sign算法變了,特開一貼用來記錄。(ps:本篇分析的APP版本爲5.4.8)

2、APP簡單分析

使用charles對目標app進行抓包,抓包結果如下:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-a4Q9ACKH-1593534162104)(2CA191D82F294673B449E9D78E9E048E)]

可以發現有個sign的簽名字段,jadx反編譯app,全局搜索sign=,找到關鍵類如下:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-iwFqD32a-1593534162106)(50EC1BF268E648C7BB5ABDC09C4A0549)]

使用frida hook a方法可知sign方法爲加簽方法,該方法爲native方法位於libnet_crypto.so中。firda 代碼如下:

var NetCrypto = Java.use("com.izuiyou.network.NetCrypto");var String = Java.use("java.lang.String");

​    NetCrypto.sign.implementation = function(arg1, arg2){var result = this.sign(arg1, arg2);

​      console.log(printBytes(arg2));

​      console.log(arg1, result);return result;}

3、SO層分析

使用ida打開libnet_crypto.so, 找到Jni_OnLoad函數,經過處理髮現動態註冊的函數位off_17D010處。

在這裏插入圖片描述

經確認該so對字符串進行了ollvm默認的加密。有以下3種處理方式:

3.1 hook register打印動態註冊的地址。

可以通過hook art.so 的register函數,打印動態註冊的地址。frida 代碼如下:

///<reference path='frida-gum.d.ts' />

// hook register 打印動態註冊的函數地址
function hook_register(){
    // libart.so 所有導出函數表
    var symbols = Module.enumerateSymbolsSync("libart.so");
    var addr_register = null;
    for(var i = 0; i < symbols.length; i++){
        var symbol = symbols[i];
        var method_name = symbol.name;
        if(method_name.indexOf("art") >= 0){
    
            if(method_name.indexOf("_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi") >= 0){
                addr_register = symbol.address;
            }
        }
    }

    // 開始hook
    if(addr_register){
        Interceptor.attach(addr_register, {
            onEnter: function(args){
                var methods = ptr(args[2]);
                var method_count = args[3];
                console.log("[RegisterNatives] method_count:", method_count);
                for(var i = 0; i < method_count; i++){
                    var fn_ptr = methods.add(i * Process.pointerSize * 3 + Process.pointerSize * 2).readPointer();
                    var find_module = Process.findModuleByAddress(fn_ptr);
                    if(i == 0){
                        console.log("module name", find_module.name);
                        console.log("module base", find_module.base);
                    }
                    console.log("\t method_name:", methods.add(i * Process.pointerSize * 3).readPointer().readCString(), "method_sign:", methods.add(i * Process.pointerSize * 3 + Process.pointerSize).readPointer().readCString(), "method_fnPtr:", fn_ptr, "method offset:", fn_ptr.sub(find_module.base));
                }
            }, onLeave(retval){

            }
        })
    }

}

function main(){
    hook_register();
}

setImmediate(main);

使用方式

frida -U --no-pause -f package_name -l hook.js

3.2 使用frida打印程序運行時已解密的字符串

APP 在運行時,會自動解密字符串。可以使用frida 對解密後的字符串進行打印,frida代碼如下:

function print_string(addr){
    var base_addr = Module.findBaseAddress("libnet_crypto.so");
    if(base_addr){
        console.log(base_addr.add(addr).readCString());
    }
}

使用frida 附加到目標APP,調用 print_string(0x17D0C1) 可得到如下結果:
在這裏插入圖片描述

那麼與其對應的sub_49864+1 就是sign方法的地址。

3.3 使用unicorn還原字符串

被“孤挺花”混淆的字符串可以用unicorn仿真器進行修復,具體原理可參考大佬的博客:https://www.leadroyal.cn/?p=972

4、定位關鍵函數

sub_49864 f5 結果如下:

在這裏插入圖片描述

鄙人定位關鍵函數的方法是使用frida hook 輸入和輸出對比結果來進行定位。最終定位的關鍵函數爲:

sub_63CB8->sub_63C1C->sub_61A70->sub_61DBA

5、還原sign

函數sub_61DBA的cfg圖如下:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-9ZTUKfA2-1593534162114)(0357D7B81B454EE1B5572D93E8F78050)]

看到這個cfg圖, 當時覺得這不白給嗎,什麼混淆都沒有。 腦子一熱一邊動態調試,一遍翻譯arm指令。 花了點時間,翻譯了500多行arm指令瞬間覺得不對勁了,這尼瑪大部分都是指令替換膨脹代碼。 粗略計算了下,該函數大概有5000條指令,這尼瑪什麼時候是頭,頂不住頂不住,爲了頭髮着想,偷了個懶,直接f5 copy僞代碼,修改了一下,發現結果對上了,果斷放棄翻譯arm彙編,有興趣的可以試下翻譯。

f5的僞代碼,有大量類似於:

(~v5 & 0xF92342E3 | v5 & 0x6DCBD1C) ^ (HIDWORD(v4) & 0x6DCBD1C | ~HIDWORD(v4) & 0xF92342E3);

的運算,可以替換成

v5 ^ (HIDWORD(v4)

等形式。

(注:Z爲常量)
(~X & Z | X & ~Z) ^ (Y & ~Z| ~Y & Z);  x ^ y
(~X & Z | X & ~Z) = X ^ ~Z = ~X ^ Z
推理過程:
    (~X & Z | X & ~Z) ^ (Y & ~Z| ~Y & Z)
  = X ^ Z ^ Y ^ Z
  = X ^ Y 

6、結果驗證

hook sign方法,獲取加密參數:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-p3AYLS4e-1593534162114)(2FF650C14FFF40B1A75C42055DEA8464)]

c運行結果圖:

在這裏插入圖片描述

7、雜談

逆過老版本的某右APP,當時的只有個sign加簽,post提交參數和返回結果還沒有aes加解密(懷念以前不穿衣服的你),整體難度應該算是容易的吧。老版本的sign沒記錯的話就是md5(待加簽的字符串進行變形),新版本的話,有點像是自己魔改的md5,不太確定,但整體感覺上是像。另外,aes加解密這塊好像是比sign難度高點,還沒去看。還發現個hook點可以讓app使用老接口,frida代碼:

    var nd3 = Java.use("nd3");
    var ia = Java.use("ia$a");
    nd3.a.overload('java.lang.String').implementation = function(arg1){
        return false;
    }
    ia.c.implementation = function(){
        var result = this.c();
        console.log(result);
        return 1;
    }

感覺該so是練習反混淆的好樣本,cfg不太複雜,想練習的朋友可以試試先trace,然後根據trace手動修復,再寫腳本還原。強烈建議各位膽大信息的大佬,去翻譯核心算法的arm彙編,試過的都說爽(逃)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章