某車聯(lián)網(wǎng)App 通訊協(xié)議加密分析(二) Unidbg手把手跑通

一、目標(biāo)

有一段時間沒有寫unidbg相關(guān)的文章了,這個樣本挺合適却音,難度適中改抡,還適當(dāng)給你挖個小坑。所以后面是一個系列文章系瓢,包含 unidbg補環(huán)境阿纤,Trace Block 對比流程,Trace Code定位差異夷陋。掌握好這一系列套路欠拾,Native分析可以算入門了。

這次先來把so用unidbg跑通

v6.1.0

二骗绕、步驟

Dump so

IDA打開 libencrypt.so 去到我們要分析的兩個函數(shù) checkcode 和 decheckcode 對應(yīng)的偏移地址 0x24424 , 0x2B1BC 藐窄。會發(fā)現(xiàn)一個奇怪的問題,這兩個地址上沒有匯編代碼酬土,都是 0x00荆忍。

估計是殼給我們加戲了,他把這兩個關(guān)鍵函數(shù)的部分代碼給抽取了撤缴,等到運行的時候才會補回去刹枉,這樣就阻礙你去靜態(tài)分析這個so。

不過殼怎么加戲屈呕,運行的時候是一定會在內(nèi)存中存在完整的代碼的微宝,否則App是跑不起來的。

所以我們也加戲虎眨,Dump 之

function dumpSo(){
    var libxx = Process.getModuleByName("libencrypt.so");
    console.log("*****************************************************");
    console.log(TAG + "name: " +libxx.name);
    console.log(TAG + "base: " +libxx.base);
    console.log(TAG + "size: " +ptr(libxx.size));

    var file_path = "/data/data/com.xxx.aeri.caranywhere/" + libxx.name + "_" + libxx.base + "_" + ptr(libxx.size) + ".so";
    console.log(TAG + file_path);

    var file_handle = new File(file_path, "wb");
    if (file_handle && file_handle != null) {
        Memory.protect(ptr(libxx.base), libxx.size, 'rwx');
        var libso_buffer = ptr(libxx.base).readByteArray(libxx.size);
        file_handle.write(libso_buffer);
        file_handle.flush();
        file_handle.close();
        console.log(TAG + "[dump]:", file_path);
    }
}

unidbg run so 基本框架

dump出so的完整代碼了蟋软,我們開始撘unidbg run so的基本框架。unidbg的庫代碼可以從原作者的github上下載最新的专甩。

public class CaranywhereDemo  extends AbstractJni {
    public AndroidEmulator emulator;
    public VM vm;
    public Module module;
    public DvmClass dvmClass;

    public static void main(String[] args) throws DecoderException, IOException {
        String apkPath = "/Users/fenfei/Desktop/xxx/6.1.0.apk";
        CaranywhereDemo carObj = new CaranywhereDemo(apkPath);
        carObj.destroy();
    }

    public CaranywhereDemo(String apkFilePath) throws DecoderException, IOException {
        //*
        emulator = AndroidEmulatorBuilder.for64Bit()
                .setProcessName("com.xxx.aeri.caranywhere")
                .addBackendFactory(new Unicorn2Factory(true))
                .build(); // 創(chuàng)建模擬器實例钟鸵,要模擬32位或者64位,在這里區(qū)分
        //*/

        // 多線程處理  true的情況下 在 faccessat  的時候就卡死涤躲, 所以關(guān)掉, 這個樣本暫時也不需要多線程
        emulator.getSyscallHandler().setEnableThreadDispatcher(false);

        final Memory memory = emulator.getMemory();                // 模擬器的內(nèi)存操作接口
        memory.setLibraryResolver(new AndroidResolver(23)); // 設(shè)置系統(tǒng)類庫解析

        vm = emulator.createDalvikVM(new File(apkFilePath)); // 創(chuàng)建Android虛擬機
        vm.setJni(this);
        vm.setVerbose(true); // 設(shè)置是否打印Jni調(diào)用細(xì)節(jié)

        new JniGraphics(emulator, vm).register(memory);
        new AndroidModule(emulator, vm).register(memory);

                dvmClass = vm.resolveClass("com/bangcle/comapiprotect/CheckCodeUtil");

                DalvikModule dm = vm.loadLibrary("encrypt", false);

        module = dm.getModule();
        dm.callJNI_OnLoad(emulator);
    }
    private void destroy() {
        try {
            emulator.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

一個unidbg的Hello World就是這樣棺耍,先別著急跑它,之前我們說過种樱,so的關(guān)鍵代碼被抽取了蒙袍,所以不能直接跑so。得換成我們dump出來的結(jié)果嫩挤。

// DalvikModule dm = vm.loadLibrary("encrypt", false);
DalvikModule dm = vm.loadLibrary(new File("/Users/fenfei/Desktop/work/blogCode/xxx/libencrypt.so_0x7634ee7000_0x1d6000.so"), false);

一步一步補unidbg run so環(huán)境

先看第一個錯誤

[07:17:09 068]  WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:384) - handleInterrupt intno=2, NR=30, svcNumber=0x16e, PC=unidbg@0xfffe0774, LR=RX@0x40018c9c[libencrypt.so]0x18c9c, syscall=null
java.lang.UnsupportedOperationException: android/app/ActivityThread->currentActivityThread()Landroid/app/ActivityThread;
        at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:432)
        at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:421)
        at com.github.unidbg.linux.android.dvm.DvmMethod.callStaticObjectMethod(DvmMethod.java:59)
        at com.github.unidbg.linux.android.dvm.DalvikVM64$111.handle(DalvikVM64.java:1723)
        at com.github.unidbg.linux.ARM64SyscallHandler.hook(ARM64SyscallHandler.java:130)

unidbg的報錯提示非常清晰害幅,說明在 callStaticObjectMethod 中調(diào)用 ActivityThread 類的靜態(tài)方法 currentActivityThread,并且返回值是 ActivityThread 類型岂昭。

我們在CaranywhereDemo.java中重載 callStaticObjectMethod 函數(shù)來解決這個問題:

@Override
    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature) {
            case "android/app/ActivityThread->currentActivityThread()Landroid/app/ActivityThread;":
                return vm.resolveClass("android/app/ActivityThread").newObject(null);
        }
        return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
    }

我們先不關(guān)心這個 currentActivityThread 要被做什么用以现,直接返回一個空的類就行。

繼續(xù)跑,下一個錯誤還是在 callStaticObjectMethod 里面邑遏,看上去像是獲取 一些系統(tǒng)信息

java.lang.UnsupportedOperationException: android/os/SystemProperties->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
        at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:432)
        at com.fenfei.test.CaranywhereDemo.callStaticObjectMethod(CaranywhereDemo.java:78)
        at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:421)
        at com.github.unidbg.linux.android.dvm.DvmMethod.callStaticObjectMethod(DvmMethod.java:59)
        at com.github.unidbg.linux.android.dvm.DalvikVM64$111.handle(DalvikVM64.java:1723)
        at com.github.unidbg.linux.ARM64SyscallHandler.hook(ARM64SyscallHandler.java:130)

補這中有入?yún)⒌暮瘮?shù)可以簡單粗暴的給他返回一個空字符串佣赖,但是講究人先要把他的入?yún)⒋蛴〕鰜怼?/p>

@Override
    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature) {
            case "android/os/SystemProperties->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;":
                System.out.println("android/os/SystemProperties->get 入?yún)ⅲ? + varArg.formatArgs());
                return new StringObject(vm, "705KPGS001091");

            case "android/app/ActivityThread->currentActivityThread()Landroid/app/ActivityThread;":
                return vm.resolveClass("android/app/ActivityThread").newObject(null);
        }
        return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
    }
Tip:

unidbg庫函數(shù)formatArgs提示不是公有函數(shù),在VarArg類里面改下 public final String formatArgs()

輸出

android/os/SystemProperties->get 入?yún)ⅲ?ro.serialno", "unknown"

原來是為了獲取Android序列號记盒,隨便給他編一個就好憎蛤, 由于本樣本只調(diào)用一次,所以就懶得判斷入?yún)⒘恕?/p>

Tip:

adb shell getprop ro.serialno 可以獲取到Android序列號

這個報錯和第一個報錯類似纪吮,不過它是在 callObjectMethod 函數(shù)里面

java.lang.UnsupportedOperationException: android/app/ActivityThread->getSystemContext()Landroid/app/ContextImpl;
        at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:921)
        at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:855)
        at com.github.unidbg.linux.android.dvm.DvmMethod.callObjectMethod(DvmMethod.java:74)
        at com.github.unidbg.linux.android.dvm.DalvikVM64$31.handle(DalvikVM64.java:504)
        at com.github.unidbg.linux.ARM64SyscallHandler.hook(ARM64SyscallHandler.java:130)

重載 callObjectMethod 函數(shù)俩檬,然后 直接返回 ContextImpl 類型就行

public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        switch (signature) {
            case "android/app/ActivityThread->getSystemContext()Landroid/app/ContextImpl;":
                return vm.resolveClass("android/app/ContextImpl").newObject(null);
        }

        return super.callObjectMethod(vm, dvmObject, signature, varArg);
    }

繼續(xù)報錯,這個是獲取包管理類

java.lang.UnsupportedOperationException: android/app/ContextImpl->getPackageManager()Landroid/content/pm/PackageManager;
        at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:921)
        at com.fenfei.test.CaranywhereDemo.callObjectMethod(CaranywhereDemo.java:91)
        at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:855)
        at com.github.unidbg.linux.android.dvm.DvmMethod.callObjectMethod(DvmMethod.java:74)
        at com.github.unidbg.linux.android.dvm.DalvikVM64$31.handle(DalvikVM64.java:504)
        at com.github.unidbg.linux.ARM64SyscallHandler.hook(ARM64SyscallHandler.java:130)

callObjectMethod 構(gòu)造 PackageManager類返回

case "android/app/ContextImpl->getPackageManager()Landroid/content/pm/PackageManager;":
                return vm.resolveClass("android/content/pm/PackageManager").newObject(null);

下一個報錯碾盟。

java.lang.UnsupportedOperationException: android/app/ContextImpl->getSystemService(Ljava/lang/String;)Ljava/lang/Object;
        at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:921)
        at com.fenfei.test.CaranywhereDemo.callObjectMethod(CaranywhereDemo.java:93)
        at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:855)
        at com.github.unidbg.linux.android.dvm.DvmMethod.callObjectMethod(DvmMethod.java:74)

這個函數(shù)有參數(shù)棚辽,老規(guī)矩,打印下參數(shù)看看巷疼。

case "android/app/ContextImpl->getSystemService(Ljava/lang/String;)Ljava/lang/Object;":
                System.out.println("android/app/ContextImpl->getSystemService 入?yún)ⅲ? + varArg.formatArgs());
                return vm.resolveClass("java/lang/Object").newObject(null);

參數(shù)打印出來是

android/app/ContextImpl->getSystemService 入?yún)ⅲ?wifi"

估摸是wifi相關(guān)晚胡,先給他返回一個空的 Object再說

后面兩個報錯就是獲取wifi Mac地址相關(guān)的,直接補上

case "android/net/wifi/WifiInfo->getMacAddress()Ljava/lang/String;":
                return new StringObject(vm, "00:00:00:00:00:00");
            case "java/lang/Object->getConnectionInfo()Landroid/net/wifi/WifiInfo;":
                return vm.resolveClass("android/net/wifi/WifiInfo").newObject(null);

到這里終于把 JNI_OnLoad 給跑通了嚼沿, 可以干一杯了。

call checkcode

搞了半天才開始進入正題瓷患,我們來調(diào)用 checkcode

public void callA() {

    String strA = "F{"appInnerVersion":"125","appOutVersion":"6.1.0","deviceType":0,"imeiMD5":"EE6431DEBB1E02FE469FA5E8467CD693","mobileModel":"GOOGLE PIXEL 2 XL","softType":"0"}";
    String strC = "1662109202156";

    String methodName = "checkcode(Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;";
    DvmObject ret = dvmClass.callStaticJniMethodObject(emulator, methodName,strA,1,strC);
    String strOut = (String)ret.getValue();
    System.out.println("call checkcode: " + strOut);
}

又有新的報錯了

java.lang.UnsupportedOperationException: android/os/Build->MODEL:Ljava/lang/String;
        at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)
        at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:53)
        at com.github.unidbg.linux.android.dvm.DvmField.getStaticObjectField(DvmField.java:106)
        at com.github.unidbg.linux.android.dvm.DalvikVM64$142.handle(DalvikVM64.java:2228)

這次要重載 getStaticObjectField 類了

public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature){
            case "android/os/Build$VERSION->SDK:Ljava/lang/String;":
                return new StringObject(vm, "23");
            case "android/os/Build->MANUFACTURER:Ljava/lang/String;":
                return new StringObject(vm, "Google");
            case "android/os/Build->MODEL:Ljava/lang/String;":
                return new StringObject(vm, "pixel");
        }
        return super.getStaticObjectField(vm,dvmClass,signature);
    }

這幾個值比較簡單骡尽,都是字符串型,我們給他賦值一把擅编。

耶攀细,大功告成了

call checkcode: FDAEKCAcOAQcNBgkEAwoCDQgEDQ4JBAgAAA4ODgcJBgkKBwYPBwwEBw4LBwsODQcFDQMMCAUJDAcEDQsADwEDDAIGDgQJAQYNDggNDQsCAQcNAwwIBQkMBwQNCwAPAQMMDQYCDAgBBQwGBAUIAwULBAoHBg8HDAQHDgsHCw4NBwUPAwEADAkPBQcODAcDDgYCDwMJCQUEAAgHDAUIBwEDBwMKDgcGBg4NDAgLBAAEAw8PAwEADAkPBQcODAcDDgYCCAQNDgkECAAADg4OBwkGCQIIAwkLBgACCgoGAgcCAwEMAQoIBw4BBw0GCQQDCgINDwMBAAwJDwUHDgwHAw4GAggHBwEMBAAAAwMJDQUDDQECBg4ECQEGDQ4IDQ0LAgEHDw0ADwMJDgQICAsJAgILBw8NAA8DCQ4ECAgLCQICCwcNBgIMCAEFDAYEBQgDBQsEAgYOBAkBBg0OCA0NCwIBBw==

先別高興的太早了,這個結(jié)果怎么看都有點不對勁爱态,和我們hook的結(jié)果相差有點大谭贪。

怎么判斷結(jié)果是對是錯?怎么和app對比來拿到正確的結(jié)果锦担? 等待下次的 Trace Block 和 Trace Code 教程吧俭识。

三、總結(jié)

unidbg補環(huán)境實際是考驗?zāi)愕腁ndroid編程能力洞渔。

谷歌一下關(guān)鍵字 unidbg + 報錯信息套媚,一般都有同道趟過坑。

什么磁椒?你打不開谷歌堤瘤?我現(xiàn)在勸你改行還來得及嗎?

ffshow.jpeg

1:ffshow

好味止園葵浆熔,大歡止稚子本辐。平生不止酒,止酒情無喜。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慎皱,一起剝皮案震驚了整個濱河市老虫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宝冕,老刑警劉巖张遭,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異地梨,居然都是意外死亡菊卷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門宝剖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洁闰,“玉大人,你說我怎么就攤上這事万细∑嗣迹” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵赖钞,是天一觀的道長腰素。 經(jīng)常有香客問我,道長雪营,這世上最難降的妖魔是什么弓千? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮献起,結(jié)果婚禮上洋访,老公的妹妹穿的比我還像新娘。我一直安慰自己谴餐,他們只是感情好姻政,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著岂嗓,像睡著了一般汁展。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上摄闸,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天善镰,我揣著相機與錄音,去河邊找鬼年枕。 笑死炫欺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的熏兄。 我是一名探鬼主播品洛,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼树姨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了桥状?” 一聲冷哼從身側(cè)響起帽揪,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辅斟,沒想到半個月后转晰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡士飒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年查邢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酵幕。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡扰藕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芳撒,到底是詐尸還是另有隱情邓深,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布笔刹,位于F島的核電站芥备,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏舌菜。R本人自食惡果不足惜门躯,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酷师。 院中可真熱鬧,春花似錦染乌、人聲如沸山孔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽台颠。三九已至,卻和暖如春勒庄,著一層夾襖步出監(jiān)牢的瞬間串前,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工实蔽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留荡碾,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓局装,卻偏偏與公主長得像坛吁,于是被迫代替她去往敵國和親劳殖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容