moji天氣逆向及unidbg實現

moji天氣逆向及unidbg實現

Java層

image-20220122095033545

還挺多結果的,不過忽略掉meizu,huawei這些第三方sdk里面的東西后,還是很容易看出來的。

com.moji.requestcore.encrypt.DefaultEncryptParamImpl.encrypt

com.moji.requestcore.encrypt.DefaultEncryptParamImpl.a

image-20220122095313009

com.moji.mjweather.library.Digest.encodeParams

image-20220122095436507

lte.NCall.IL

image-20220122095529619

其實libGameVMP.so對我來說不陌生,之前在看某物 dewu app so newSign 參數分析破解這篇文章的時候,因為它奇葩的特性所以記住了。所以在moji這個app是不是也一樣?

先hook一下com.moji.mjweather.library.Digest這個類

android hooking watch class com.moji.mjweather.library.Digest
image-20220122100833766

可以看到每次調用encodeParams的時候,nativeEncodeParams都被調用了。

再hook一下nativeEncodeParams這個函數。

android hooking watch class_method com.moji.mjweather.library.Digest.nativeEncodeParams --dump-args --dump-return

從hook結果可以看到最后確實是調用它生成sign。

image-20220122101503411

這么多個so,哪個才是調用加密的呢?從名字看libencrypt.solibEncryptor.so最可疑。

jnitrace看看。

jnitrace.exe -l libencrypt.so -l libEncryptor.so com.moji.mjweather|tee trace-moji.txt

然后嘗試在記錄里搜索某個簽名

image-20220122102026676

可以看到最終是在libencrypt.so生成sign的。

so層

從地址可以看出so是64位的,因為手機和apk都支持64位指令,接下來的分析也是針對64位。

打開arm64-v8a下的libencrypt.so,函數窗口搜索java

image-20220122103036809

說明是動態注冊的,看看JNI_OnLoad

image-20220122103121420

跳轉過去

image-20220122103300225

繼續跳轉

image-20220122103402413

所以真實函數名是parseParams

第二種方法是frida_hook_libart

frida -U -f com.moji.mjweather -l hook_RegisterNatives.js --no-pause
image-20220122103819411

然后跳轉到0x3d1a0

image-20220122103916475

另一種方法就是用jnitrace

jnitrace -l libencrypt.so -i RegisterNatives com.moji.mjweather
image-20220122104046572

函數地址0x728b2861a0減去so基地址0x728b249000等于0x3d1a0,跳轉過去同樣也是parseParams

image-20220122104433356

算法其實挺簡單的,就是后面拼接個字符串,然后做個MD5就出結果了,可以自行hook MD5Update函數進行驗證,這里就省略了。

unidbg實現

由于unidbg可以自由選擇32還是64位,這里選擇的是32位的。先搭個框架

public class Moji extends AbstractJni {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    public static String pkgName = "com.moji.mjweather";
    public static String apkPath = "unidbg-android/src/test/java/com/moji/moji90300.apk";
    public static String soPath = "";

    public Moji() {
        emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(pkgName).build();
        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File(apkPath));
        vm.setJni(this);
        vm.setVerbose(true);
        DalvikModule dm = vm.loadLibrary("encrypt", true);
        module = dm.getModule();
        dm.callJNI_OnLoad(emulator);
    }
    
    public static void main(String[] args) {
        Moji test = new Moji();
    }
}

開始報錯和補函數。

image-20220122105734300
@Override
public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
    switch (signature) {
        case "com/moji/tool/AppDelegate->getAppContext()Landroid/content/Context;": {
            return vm.resolveClass("android/content/Context").newObject(null);
        }
    }
    return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
}
image-20220122105849621
@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
    switch (signature) {
        case "android/app/ActivityThread->sCurrentActivityThread:Landroid/app/ActivityThread;": {
            return vm.resolveClass("android/app/ActivityThread").newObject(null);
        }
    }
    return super.getStaticObjectField(vm, dvmClass, signature);
}
image-20220122110001747

不知道怎么補,開始擺爛

public void setStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature, DvmObject<?> value) {
    switch (signature) {
        case "android/app/ActivityThread->sPackageManager:Landroid/content/pm/IPackageManager;": {

        }
    }
}
image-20220122110156418
case "android/os/ServiceManager->getService(Ljava/lang/String;)Landroid/os/IBinder;": {
    return vm.resolveClass("android/os/IBinder").newObject(null);
}
image-20220122110302330

越來越離譜,一怒之下,決定不補了,嘗試直接patch。

image-20220122110444905

跳轉到之前獲取PackageManager的地方,也就是0x11315

image-20220122110652365
image-20220122110806795

可以看出getPublicKey這個函數主要起獲取簽名的作用,X查看查看引用

image-20220122110857747

看看checkPubKey

image-20220122111106477

可以看出校驗成功會返回1,失敗返回0。

查看checkPubKey的引用

image-20220122111907460

可以看到在關鍵函數中都有調用,先看看checkKeyOnLoad(因為對它查看引用的話,可以看到它在JNI_OnLoad被調用了)

image-20220122112045492
image-20220122112159491

直接修改0x117A2這條指令,把返回值,也就是r0的值改為1

image-20220122112341603
public void patchVerify(){
    emulator.getMemory().pointer(module.base+0x117A2).setInt(0, 0x4ff00100);
}
public Moji() {
    // ...
    this.patchVerify();
    dm.callJNI_OnLoad(emulator);
}

再次跑起來。

image-20220122112634268

JNI_OnLoad完成了,接下來是正式調用。

public void call_sign() {
    List<Object> list = new ArrayList<>(10);
    list.add(vm.getJNIEnv());
    list.add(0);
    list.add(vm.addLocalObject(new StringObject(vm, "1")));
    Number ret = module.callFunction(emulator, 0x21a9d, list.toArray());
    System.out.println(vm.getObject(ret.intValue()).getValue().toString());
}
public static void main(String[] args) {
    Moji test = new Moji();
    test.call_sign();
}
image-20220122112859178

又報錯了,這是因為之前的checkPubKeyparseParams中也被調用了

image-20220122113026061
image-20220122113052683

同樣的,patch這條指令

public void patchVerify(){
    emulator.getMemory().pointer(module.base+0x117A2).setInt(0, 0x4ff00100);
    emulator.getMemory().pointer(module.base+0x21AF4).setInt(0, 0x4ff00100);
}

重新運行,結果就出來了

image-20220122113215092

完整代碼

package com.moji;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
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.memory.Memory;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class Moji extends AbstractJni {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    public static String pkgName = "com.moji.mjweather";
    public static String apkPath = "unidbg-android/src/test/java/com/moji/moji90300.apk";
    public static String soPath = "";

    public Moji() {
        emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(pkgName).build();
        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File(apkPath));
        vm.setJni(this);
        vm.setVerbose(true);
        DalvikModule dm = vm.loadLibrary("encrypt", true);
        module = dm.getModule();
        this.patchVerify();
        dm.callJNI_OnLoad(emulator);
    }

    public void patchVerify(){
        emulator.getMemory().pointer(module.base+0x117A2).setInt(0, 0x4ff00100);
        emulator.getMemory().pointer(module.base+0x21AF4).setInt(0, 0x4ff00100);
    }

    @Override
    public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        switch (signature) {
            case "com/moji/tool/AppDelegate->getAppContext()Landroid/content/Context;": {
                return vm.resolveClass("android/content/Context").newObject(null);
            }
        }
        return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
    }

    public void call_sign() {
        List<Object> list = new ArrayList<>(10);
        list.add(vm.getJNIEnv());
        list.add(0);
        list.add(vm.addLocalObject(new StringObject(vm, "1")));
        Number ret = module.callFunction(emulator, 0x21a9d, list.toArray());
        System.out.println(vm.getObject(ret.intValue()).getValue().toString());
    }

    public static void main(String[] args) {
        Moji test = new Moji();
        test.call_sign();
    }
}

總結

  1. patch一時爽,一直patch一直爽。不過有哪位補函數的高手能完整補出來嗎,想學習學習
  2. 有沒有方法能讓64位手機,64位app運行在32位環境,習慣了32位環境,分析64位不太習慣

Update 2022-02-09

Android adb安裝時強制應用App以32位或者64位運行

指定應用在64位終端下以32位方式模式下運行

adb install --abi armeabi-v7a <path to apk>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內容