京東sign unidbg逆向

京東sign unidbg逆向

環(huán)境

jdk 1.8.0_311

app 9.2.2

unidbg還原

新建包和類

image-20211207144715386

添加代碼

package com.jingdong;

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.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger;
import com.github.unidbg.memory.Memory;
import sun.security.pkcs.PKCS7;
import sun.security.pkcs.ParsingException;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.security.cert.X509Certificate;

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

    public static String pkgName = "com.jingdong.app.mall";
    public static String apkPath = "unidbg-android/src/test/java/com/jingdong/jingdong9.2.2.apk";
    public static String soPath = "unidbg-android/src/test/java/com/jingdong/libjdbitmapkit.so";
    private static final String APK_PATH = "/data/app/com.jingdong.app.mall.apk";

    JingDong() {
        emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(pkgName).build();
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File(apkPath));
        DalvikModule dm = vm.loadLibrary(new File(soPath), false);
        vm.setJni(this);
        vm.setVerbose(true);
        dm.callJNI_OnLoad(emulator);
        module = dm.getModule();

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

運(yùn)行后報錯

image-20211207145009012

補(bǔ)上方法

// 錯誤版本
@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
    switch (signature) {
        case "com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;": {
            return vm.resolveClass("android/app/Application").newObject(null);
        }
    }
    return super.getStaticObjectField(vm, dvmClass, signature);
}

運(yùn)行失敗

image-20211207145153158

說實(shí)話,沒太看懂什么問題阔馋,于是上網(wǎng)搜了下团秽,在Illegal JNI version: 0xffffffff · Issue #315 · zhkl0228/unidbg (github.com)這個unidbg的issue下找到了unidbg作者對京東逆向的實(shí)現(xiàn)峭拘。此外,還有Unidbg模擬執(zhí)行大廠so實(shí)操教程(二) - 奮飛安全 (91fans.com.cn)這篇文章提到了類似的問題腕唧。

@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
    switch (signature) {
        case "com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;": {
            return vm.resolveClass("android/app/Activity", vm.resolveClass("android/content/ContextWrapper", vm.resolveClass("android/content/Context"))).newObject(null);
        }
    }
    return super.getStaticObjectField(vm, dvmClass, signature);
}

運(yùn)行后有新的問題

image-20211207145957234

補(bǔ)上

@Override
public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
    switch (signature) {
        case "android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;": {
            return new StringObject(vm, APK_PATH);
        }
    }
    return super.getObjectField(vm, dvmObject, signature);
}

新的問題+1

image-20211207150033297
@Override
public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
    switch (signature) {
        case "com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B": {
            StringObject apkPath = varArg.getObjectArg(0);
            StringObject directory = varArg.getObjectArg(1);
            StringObject filename = varArg.getObjectArg(2);
            if (APK_PATH.equals(apkPath.getValue()) &&
                "META-INF/".equals(directory.getValue()) &&
                ".RSA".equals(filename.getValue())) {
                byte[] data = vm.unzip("META-INF/JINGDONG.RSA");
                return new ByteArray(vm, data);
            }
        }
    }
    return super.callStaticObjectMethod(vm ,dvmClass, signature, varArg);
}

新的問題+1

image-20211207150326585
@Override
public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
    switch (signature) {
        case "sun/security/pkcs/PKCS7-><init>([B)V": {
            ByteArray array = varArg.getObjectArg(0);
            try {
                return vm.resolveClass("sun/security/pkcs/PKCS7").newObject(new PKCS7(array.getValue()));
            } catch (ParsingException e) {
                throw new IllegalStateException(e);
            }
        }
    }
    return super.newObject(vm, dvmClass, signature, varArg);
}

這里使用jdk1.8可以直接運(yùn)行翎朱,使用jdk1.16會報sun.security.pkcs.PKCS7的相關(guān)問題,請自己解決(因?yàn)槲乙膊恢涝趺唇鉀Q全释,才會直接換到j(luò)dk1.8装处。。)

新的問題+1

image-20211207150846665
@Override
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
    switch (signature) {
        case "sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;": {
            PKCS7 pkcs7 = (PKCS7) dvmObject.getValue();
            X509Certificate[] certificates = pkcs7.getCertificates();
            return ProxyDvmObject.createObject(vm, certificates);
        }
    }
    return super.callObjectMethod(vm, dvmObject, signature, varArg);
}

新的問題+1

image-20211207151057496
case "com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B": {
    DvmObject<?> obj = varArg.getObjectArg(0);
    byte[] bytes = objectToBytes(obj.getValue());
    return new ByteArray(vm, bytes);
}
private static byte[] objectToBytes(Object obj) {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.flush();
        byte[] array = baos.toByteArray();
        oos.close();
        baos.close();
        return array;
    } catch (IOException e) {
        throw new IllegalStateException(e);
    }
}
image-20211207151231282

重新運(yùn)行后浸船,發(fā)現(xiàn)沒有報錯了妄迁,至此,對基礎(chǔ)環(huán)境的修補(bǔ)完成李命,現(xiàn)在開始調(diào)用函數(shù)登淘。

public void callSign() {
    DvmClass cBitmapkitUtils = vm.resolveClass("com/jingdong/common/utils/BitmapkitUtils");
    StringObject ret = cBitmapkitUtils.callStaticJniMethodObject(emulator, "getSignFromJni()(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
            vm.resolveClass("android/content/Context").newObject(null),
            "clientImage",
            "{\"moduleParams\":{\"18\":\"1565611060638\",\"19\":\"1565229712150\",\"25\":\"1567478504636\",\"27\":\"1602488415048\",\"28\":\"1631069159956\",\"30\":\"1567404005627\",\"32\":\"1567997588476\",\"34\":\"1593508185597\",\"35\":\"1568708316462\",\"37\":\"1630293538664\",\"42\":\"1623741761542\",\"44\":\"1569247647090\",\"46\":\"1588839806224\",\"47\":\"1571295610042\",\"61\":\"1582091758495\",\"70\":\"1585279774645\",\"74\":\"1586781606615\"}}",
            "d5a585639f505b18",
            "android",
            "10.2.0");
    System.out.println(ret.getValue());
}

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

又有新的問題,繼續(xù)補(bǔ)環(huán)境

image-20211207151934366
@Override
public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
    switch (signature) {
        case "java/lang/StringBuffer-><init>()V": {
            return vm.resolveClass("java/lang/StringBuffer").newObject(new StringBuffer());
        }
    }
    return super.newObjectV(vm, dvmClass, signature, vaList);
}

新的問題+1

image-20211207152056521
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
    switch (signature) {
        case "java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;": {
            StringBuffer buffer = (StringBuffer) dvmObject.getValue();
            StringObject str = vaList.getObjectArg(0);
            buffer.append(str.getValue());
            return dvmObject;
        }
    }
    return super.callObjectMethodV(vm, dvmObject, signature, vaList);
}

新的問題+1

image-20211207152254815
case "java/lang/Integer-><init>(I)V": {
    return DvmInteger.valueOf(vm, vaList.getIntArg(0));
}

新的問題+1

image-20211207152344079
case "java/lang/Integer->toString()Ljava/lang/String;": {
    return new StringObject(vm, ((Integer)dvmObject.getValue()).toString());
}

新的問題+1

image-20211207152423024
case "java/lang/StringBuffer->toString()Ljava/lang/String;": {
    return new StringObject(vm, ((StringBuffer)dvmObject.getValue()).toString());
}

再次運(yùn)行

image-20211207152534833

淚目封字,終于出結(jié)果了黔州。

完整代碼

package com.jingdong;

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.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger;
import com.github.unidbg.memory.Memory;
import sun.security.pkcs.PKCS7;
import sun.security.pkcs.ParsingException;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.security.cert.X509Certificate;

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

    public static String pkgName = "com.jingdong.app.mall";
    public static String apkPath = "unidbg-android/src/test/java/com/jingdong/jingdong9.2.2.apk";
    public static String soPath = "unidbg-android/src/test/java/com/jingdong/libjdbitmapkit.so";
    private static final String APK_PATH = "/data/app/com.jingdong.app.mall.apk";

    JingDong() {
        emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(pkgName).build();
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File(apkPath));
        DalvikModule dm = vm.loadLibrary(new File(soPath), false);
        vm.setJni(this);
        vm.setVerbose(true);
        dm.callJNI_OnLoad(emulator);
        module = dm.getModule();
    }

    @Override
    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature) {
            case "com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;": {
                return vm.resolveClass("android/app/Activity", vm.resolveClass("android/content/ContextWrapper", vm.resolveClass("android/content/Context"))).newObject(null);
            }
        }
        return super.getStaticObjectField(vm, dvmClass, signature);
    }

    @Override
    public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
        switch (signature) {
            case "android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;": {
                return new StringObject(vm, APK_PATH);
            }
        }
        return super.getObjectField(vm, dvmObject, signature);
    }

    @Override
    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature) {
            case "com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B": {
                StringObject apkPath = varArg.getObjectArg(0);
                StringObject directory = varArg.getObjectArg(1);
                StringObject filename = varArg.getObjectArg(2);
                if (APK_PATH.equals(apkPath.getValue()) &&
                        "META-INF/".equals(directory.getValue()) &&
                        ".RSA".equals(filename.getValue())) {
                    byte[] data = vm.unzip("META-INF/JINGDONG.RSA");
                    return new ByteArray(vm, data);
                }
            }
            case "com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B": {
                DvmObject<?> obj = varArg.getObjectArg(0);
                byte[] bytes = objectToBytes(obj.getValue());
                return new ByteArray(vm, bytes);
            }
        }
        return super.callStaticObjectMethod(vm ,dvmClass, signature, varArg);
    }

    @Override
    public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature) {
            case "sun/security/pkcs/PKCS7-><init>([B)V": {
                ByteArray array = varArg.getObjectArg(0);
                try {
                    return vm.resolveClass("sun/security/pkcs/PKCS7").newObject(new PKCS7(array.getValue()));
                } catch (ParsingException e) {
                    throw new IllegalStateException(e);
                }
            }
        }
        return super.newObject(vm, dvmClass, signature, varArg);
    }

    @Override
    public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        switch (signature) {
            case "sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;": {
                PKCS7 pkcs7 = (PKCS7) dvmObject.getValue();
                X509Certificate[] certificates = pkcs7.getCertificates();
                return ProxyDvmObject.createObject(vm, certificates);
            }
        }
        return super.callObjectMethod(vm, dvmObject, signature, varArg);
    }

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

    @Override
    public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
        switch (signature) {
            case "java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;": {
                StringBuffer buffer = (StringBuffer) dvmObject.getValue();
                StringObject str = vaList.getObjectArg(0);
                buffer.append(str.getValue());
                return dvmObject;
            }
            case "java/lang/Integer->toString()Ljava/lang/String;": {
                return new StringObject(vm, ((Integer)dvmObject.getValue()).toString());
            }
            case "java/lang/StringBuffer->toString()Ljava/lang/String;": {
                return new StringObject(vm, ((StringBuffer)dvmObject.getValue()).toString());
            }
        }
        return super.callObjectMethodV(vm, dvmObject, signature, vaList);
    }

    private static byte[] objectToBytes(Object obj) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.flush();
            byte[] array = baos.toByteArray();
            oos.close();
            baos.close();
            return array;
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    public void callSign() {
        DvmClass cBitmapkitUtils = vm.resolveClass("com/jingdong/common/utils/BitmapkitUtils");
        StringObject ret = cBitmapkitUtils.callStaticJniMethodObject(emulator, "getSignFromJni()(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
                vm.resolveClass("android/content/Context").newObject(null),
                "clientImage",
                "{\"moduleParams\":{\"18\":\"1565611060638\",\"19\":\"1565229712150\",\"25\":\"1567478504636\",\"27\":\"1602488415048\",\"28\":\"1631069159956\",\"30\":\"1567404005627\",\"32\":\"1567997588476\",\"34\":\"1593508185597\",\"35\":\"1568708316462\",\"37\":\"1630293538664\",\"42\":\"1623741761542\",\"44\":\"1569247647090\",\"46\":\"1588839806224\",\"47\":\"1571295610042\",\"61\":\"1582091758495\",\"70\":\"1585279774645\",\"74\":\"1586781606615\"}}",
                "d5a585639f505b18",
                "android",
                "10.2.0");
        System.out.println(ret.getValue());
    }

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

其他

unidbg逆向就是個補(bǔ)環(huán)境的工作,前面我們補(bǔ)環(huán)境的時候阔籽,其實(shí)可以將一些通用的方法在父類中實(shí)現(xiàn)流妻,比如對StringBuilder的操作,對Integer的操作笆制,而不是選擇在子類中實(shí)現(xiàn)绅这,這樣就不用每次逆向的時候都要寫同樣的代碼。

具體來說在辆,就是在unidbg-android/src/main/java/com/github/unidbg/linux/android/dvm/AbstractJni.java文件中添加對應(yīng)的代碼

image-20211207153642466

上面這些是unidbg的作者已經(jīng)實(shí)現(xiàn)的方法证薇,我們只需要在后面補(bǔ)上需要實(shí)現(xiàn)的方法即可。當(dāng)然匆篓,保證你的實(shí)現(xiàn)的方式具有通用性浑度,不然在逆向新的app的時候出了問題的話一時間很難察覺。

代碼僅供把玩鸦概。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末箩张,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子窗市,更是在濱河造成了極大的恐慌先慷,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谨设,死亡現(xiàn)場離奇詭異熟掂,居然都是意外死亡缎浇,警方通過查閱死者的電腦和手機(jī)扎拣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人二蓝,你說我怎么就攤上這事誉券。” “怎么了刊愚?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵踊跟,是天一觀的道長。 經(jīng)常有香客問我鸥诽,道長商玫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任牡借,我火速辦了婚禮拳昌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钠龙。我一直安慰自己炬藤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布碴里。 她就那樣靜靜地躺著沈矿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咬腋。 梳的紋絲不亂的頭發(fā)上羹膳,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機(jī)與錄音帝火,去河邊找鬼溜徙。 笑死,一個胖子當(dāng)著我的面吹牛犀填,可吹牛的內(nèi)容都是我干的蠢壹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼九巡,長吁一口氣:“原來是場噩夢啊……” “哼图贸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起冕广,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤疏日,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后撒汉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沟优,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年睬辐,在試婚紗的時候發(fā)現(xiàn)自己被綠了挠阁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宾肺。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖侵俗,靈堂內(nèi)的尸體忽然破棺而出锨用,到底是詐尸還是另有隱情,我是刑警寧澤隘谣,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布增拥,位于F島的核電站,受9級特大地震影響寻歧,放射性物質(zhì)發(fā)生泄漏掌栅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一码泛、第九天 我趴在偏房一處隱蔽的房頂上張望渣玲。 院中可真熱鬧,春花似錦弟晚、人聲如沸忘衍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽枚钓。三九已至,卻和暖如春瑟押,著一層夾襖步出監(jiān)牢的瞬間搀捷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工多望, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嫩舟,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓怀偷,卻偏偏與公主長得像家厌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子椎工,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

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