app逆向之安卓native層安全逆向分析(八):unidbg補前置環境+io重定向

前言

繼續跟着龍哥的unidbg學習:SO逆向入門實戰教程七:main_unidbg 重定向_白龍~的博客-CSDN博客

還是那句,我會借鑑龍哥的文章,以一個初學者的角度,加上自己的理解,把內容豐富一下,儘量做到不在龍哥的基礎上畫蛇添足。感謝觀看的朋友。

分析

首先,抓個包

 

裏面這個mtgsig就是該app很經典的加密參數了,siua參數後續有時間就分析,沒有就算了。本篇文章的重點是mtgsig.

 

1.靜態分析

jadx分析,一搜:

 

發現其實並不多。

 

 

就拿這幾個類進行hook,看看哪些方法被調用了就行了,沒花多久時間,就找到這裏:

 

 

 com.xxxxx.plugin.sign.core.CandyPreprocessor.makeHeader(Uri$Builder) String

 

再跟一下:

 

 

 

 

 

這個main就是我們要的部位了。我們知道,第一個int參數,其實都寫死了,就是203

 

第二個是一個對象數組

 

 hook下這兩個方法,main的地方參數中間有個byte[]沒有打印出來

 

 

2.frida動態調試 

 

 

 

 

所以這個byte[]就是個 請求方式  url的path  子路徑

 

 

好了,入參大概都知道了 

ida調試

 

調試之前,得找找他加載的是哪個so文件

 

這靜態看代碼,看了半天,愣是沒看出來哪裏用了system.loadlibrary。

 

用這個grep找到的是這個so,其實並不是

 

 

這裏繼續用yang神的hook_RegisterNative.js,一頓輸出,直接找到這個so:

 

 

很nice。

臥槽,跑完直接把我手機幹關機了。有點6

 

接下來打開ida看看,導出表都不用看了

 

 

 

 方法也看不到啊,上面用hook_RegisterNative已經hook到位置了,直接定位過去就行

 結果就是解析異常了。

unidbg調試

那直接unidbg調試吧

 

1.搭架子

跑起來,好像沒啥問題 ,main也有了。

 

2.調用

那麼直接調用吧,首先把之前hook到的數據,傳遞進去

 

上面需要的都有hook了,

 

現在就差這個key,往上找找:

 

 

hook下,等會兒,還hook啥呀,前面已經有了,就是這個9b69xxxx,思路不能亂,慢慢來

 

開始調用,結果如下:

 

 

尷尬,報錯的意思是這個指針結果是一個空指針。根據龍哥的博客,應該是上下文缺失。jnitrace也可以發現。

 

 

3.解決問題

再來看看,這個main的調用實例

 

有這麼多

用fridahook下這些

 

 

發現,果然是了,剛開始調用,還是null,然後再次調用,就有值了。

 

再來,把入參打印一下:

 

 

看來第一次是111,也就是代碼的這裏:

 

 

 

 這個好像還算簡單。

 

補一下這裏的前置條件,然後執行:

 

補下這個環境,但是這個是啥呢,直接用firda hook可知如下:

 因爲他是static

 

 

那麼直接調用

 

補完執行:

 

 

又來一個新的,同樣操作

 

 

還有報錯,

 

 

繼續

 

 

 

臥槽,還有報錯,而且這個報錯,龍哥的博客好像沒有這個,我手機是米10,安卓12,估計有啥不一樣

 

問題不大,他既然要返回一個i,也就是整形,那直接這麼補,然後執行:

 

說白了這個就是要獲取當前的進程apk的路徑,繼續補

 

沒啥可說的,繼續補:

 

 

 

繼續

 

 

 

繼續

 

好了,終於不報錯了,但是結果並沒有被打印出來

 

仔細看這裏,根據龍哥的博客,意思是這裏做了文件讀取操作,但是-100,好像讀取異常了

 

 

這時候有兩種方案解決這個

  • 到unidbg目錄,把apk放進去,用這個文件src/main/java/com/github/unidbg/file/BaseFileSystem.java獲取當前程序路徑
  • 還可以用代碼的方式進行操作,設置io重定向,把訪問的路徑放到我們指定的路徑即可

 

這裏我用重定向的方式:

 

 

然後執行:

 

 

繼續補

 

 

但這裏出現了問題,這裏是一個空,尷尬,查看用例:

 

就這裏做了賦值

 

 

想hook一下,結果hook不到,尷尬,但是那邊確實看到是空,龍哥的博客是有數據的,不重要,遵從內心,補一個空,執行看看:

 

 

唉,結果有了,好像結果數據有點不對,最後是一個dvmobject,那就跟前面的文章一樣了,轉一下,打印下

 

這兩個方式都可以:

 

 

這個結果能不能用呢?

重新抓個包,然後替換下試試:

 

 

 

ok,果然可以。很nice

 

代碼

 

package com.sankuai;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.api.Binder;
import com.github.unidbg.linux.android.dvm.api.ServiceManager;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;


import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class meituan extends AbstractJni implements IOResolver {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    meituan() {
        // 創建模擬器實例,進程名建議依照實際進程名填寫,可以規避針對進程名的校驗
        emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.sankuai.meituan").build();
        // 獲取模擬器的內存操作接口
        final Memory memory = emulator.getMemory();
//        emulator.getSyscallHandler().setEnableThreadDispatcher(true);
        // 設置系統類庫解析
        memory.setLibraryResolver(new AndroidResolver(23));
        // 創建Android虛擬機,傳入APK,Unidbg可以替我們做部分簽名校驗的工作
        vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\xxx\\xxx.apk"));
        // 加載目標SO
//        DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\xxxx\\libmtguard.so"), true); // 加載so到虛擬內存

        emulator.getSyscallHandler().addIOResolver(this);
        vm.setVerbose(true); // 設置是否打印Jni調用細節
        DalvikModule dm = vm.loadLibrary(new File("E:\\work\\myproject\\unidbg\\unidbg-android\\src\\test\\java\\com\\xxxx\\libmtguard.so"), true);


        //獲取本SO模塊的句柄,後續需要用它
        module = dm.getModule();
        vm.setJni(this); // 設置JNI
        vm.setVerbose(true); // 打印日誌
        dm.callJNI_OnLoad(emulator); // 調用JNI OnLoad
    }


    @Override
    public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        switch (signature) {
            case "com/meituan/android/common/mtguard/NBridge->getPicName()Ljava/lang/String;":
                return new StringObject(vm, "ms_com.xxx.xxx");
            case "com/meituan/android/common/mtguard/NBridge->getSecName()Ljava/lang/String;":
                return new StringObject(vm, "ppd_com.xxx.xxx.xbt");
            case "com/xxxx/android/common/mtguard/NBridge->getAppContext()Landroid/content/Context;":
                return vm.resolveClass("android/content/Context").newObject(null);
            case "com/xxxx/android/common/mtguard/NBridge->getMtgVN()Ljava/lang/String;":
                return new StringObject(vm, "4.4.7.3");
            case "com/xxx/android/common/mtguard/NBridge->getDfpId()Ljava/lang/String;":
                return new StringObject(vm, "");


        }

        return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
    }

    @Override
    public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        switch (signature) {
            case "java/lang/Integer-><init>(I)V": {
                int input = vaList.getIntArg(0);
                return vm.resolveClass("java/lang/Integer").newObject(input);
            }
        }

        return super.newObjectV(vm, dvmClass, signature, vaList);
    }

    @Override
    public int getStaticIntField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature) {
            case "android/content/pm/PackageManager->GET_SIGNATURES:I":
                return 1;

        }
        return super.getStaticIntField(vm, dvmClass, signature);
    }

    @Override
    public int getIntField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
        switch (signature) {
            case "android/content/pm/PackageInfo->versionCode:I":
                return 2;

        }
        return super.getIntField(vm, dvmObject, signature);
    }


    @Override
    public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
        switch (signature) {
            case "android/content/Context->getPackageCodePath()Ljava/lang/String;":
                return new StringObject(vm, "/data/app/com.xxx.xxx-TEfTAIBttUmUzuVbwRK1DQ==/base.apk");
        }
        return super.callObjectMethodV(vm, dvmObject, signature, vaList);
    }

    public static void main(String[] args) {
        meituan test = new meituan();
        test.callMain111();
        System.out.println(test.getSign());
    }

    public void callMain111() {
        List<Object> list = new ArrayList<>(10);
        list.add(vm.getJNIEnv()); // arg1
        list.add(0); //arg2
        list.add(111); //arg3
        DvmObject<?> obj = vm.resolveClass("java/lang/object").newObject(null);
        vm.addLocalObject(obj);
        // 完整的參數2
        list.add(vm.addLocalObject(new ArrayObject(obj)));
        Number number = module.callFunction(emulator, 0x5a38d, list.toArray());
    }

    public String getSign() {
        List<Object> list = new ArrayList<>(10);
        list.add(vm.getJNIEnv()); // arg1
        list.add(0); //arg2
        list.add(203); //arg3
        StringObject input2_1 = new StringObject(vm, "9b69f861-e054-4bc4-9daf-d36ae205ed3e");
        ByteArray input2_2 = new ByteArray(vm, "GET /api/entry/indexLayer __reqTraceID=aa14663c-9979-445b-a311-8017c596f343&ci=1&group=xxxx&msid=xxxxx&userid=-1&utm_campaign=AgroupBgroupC0E0&utm_content=xxxx&utm_medium=android&utm_source=wandoujia&utm_term=1100090405&uuid=xxxxx&version_name=11.9.405".getBytes(StandardCharsets.UTF_8));
        DvmInteger input2_3 = DvmInteger.valueOf(vm, 2);
        vm.addLocalObject(input2_1);
        vm.addLocalObject(input2_2);
        vm.addLocalObject(input2_3);
        // 完整的參數2
        list.add(vm.addLocalObject(new ArrayObject(input2_1, input2_2, input2_3)));
        Number number = module.callFunction(emulator, 0x5a38d, list.toArray());
//        String result = vm.getObject(number.intValue()).getValue().toString();
//        return result;

        DvmObject[] result1 = (DvmObject[]) vm.getObject(number.intValue()).getValue();
        String sign = result1[0].toString();
        System.out.println(sign);

        StringObject result2 = (StringObject) ((DvmObject[])((ArrayObject)vm.getObject(number.intValue())).getValue())[0];
        System.out.println(result2.getValue());
        return result2.getValue();
    }

    @Override
    public FileResult resolve(Emulator emulator, String pathname, int oflags) {
        if (("/data/app/com.sankuai.meituan-TEfTAIBttUmUzuVbwRK1DQ==/base.apk").equals(pathname)) {
            // 填入想要重定位的文件
            return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android\\src\\test\\java\\com\\xxx\\xxx.apk"), pathname));
        }
        return null;
    }
}

 

 

 

知識點總結

 

1.找不到該native方法引入的哪個so文件時,用hook_RegisterNative腳本分析

2.執行結果發現不對返回空指針時,很大可能是邏輯問題,需要結合jnitrace分析

3.報dirfd=-100,就是讀取文件錯誤,需要指導程序到正確的路徑

4.補文件讀取有兩個方法

  • 到unidbg目錄,把apk放進去,用這個文件src/main/java/com/github/unidbg/file/BaseFileSystem.java獲取當前程序路徑
  • 還可以用代碼的方式進行操作,設置io重定向,把訪問的路徑放到我們指定的路徑即可

5.補環境的時候getIntArg是獲取方法的參數

我的代碼是:

int input = vaList.getIntArg(0);
return vm.resolveClass("java/lang/Integer").newObject(input);

  

龍哥的代碼:

int input = vaList.getInt(0);
return new DvmInteger(vm, input);

  

 

6.如果最後的數據是一個數組而不是字符串,有兩種方式獲取

 

StringObject result = (StringObject) ((DvmObject[])((ArrayObject)vm.getObject(number.intValue())).getValue())[0];
System.out.println(result.getValue());

 

 

DvmObject[] result1 = (DvmObject[]) vm.getObject(number.intValue()).getValue();
String sign = result1[0].toString();
System.out.println(sign);

 

 

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