京东sign unidbg逆向

京东sign unidbg逆向

环境

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();
    }
}

运行后报错

image-20211207145009012

补上方法

// 错误版本
@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);
}

运行失败

image-20211207145153158

说实话,没太看懂什么问题,于是上网搜了下,在Illegal JNI version: 0xffffffff · Issue #315 · zhkl0228/unidbg (github.com)这个unidbg的issue下找到了unidbg作者对京东逆向的实现。此外,还有Unidbg模拟执行大厂so实操教程(二) - 奋飞安全 (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);
}

运行后有新的问题

image-20211207145957234

补上

@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->([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可以直接运行,使用jdk1.16会报sun.security.pkcs.PKCS7的相关问题,请自己解决(因为我也不知道怎么解决,才会直接换到jdk1.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

重新运行后,发现没有报错了,至此,对基础环境的修补完成,现在开始调用函数。

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();
}

又有新的问题,继续补环境

image-20211207151934366
@Override
public DvmObject newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
    switch (signature) {
        case "java/lang/StringBuffer->()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->(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());
}

再次运行

image-20211207152534833

泪目,终于出结果了。

完整代码

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->([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->()V": {
                return vm.resolveClass("java/lang/StringBuffer").newObject(new StringBuffer());
            }
            case "java/lang/Integer->(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逆向就是个补环境的工作,前面我们补环境的时候,其实可以将一些通用的方法在父类中实现,比如对StringBuilder的操作,对Integer的操作,而不是选择在子类中实现,这样就不用每次逆向的时候都要写同样的代码。

具体来说,就是在unidbg-android/src/main/java/com/github/unidbg/linux/android/dvm/AbstractJni.java文件中添加对应的代码

image-20211207153642466

上面这些是unidbg的作者已经实现的方法,我们只需要在后面补上需要实现的方法即可。当然,保证你的实现的方式具有通用性,不然在逆向新的app的时候出了问题的话一时间很难察觉。

代码仅供把玩。

你可能感兴趣的:(京东sign unidbg逆向)