京東sign unidbg逆向
環(huán)境
jdk 1.8.0_311
app 9.2.2
unidbg還原
新建包和類
添加代碼
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)行后報錯
補(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)行失敗
說實(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)行后有新的問題
補(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
@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
@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
@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
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);
}
}
重新運(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)境
@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
@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
case "java/lang/Integer-><init>(I)V": {
return DvmInteger.valueOf(vm, vaList.getIntArg(0));
}
新的問題+1
case "java/lang/Integer->toString()Ljava/lang/String;": {
return new StringObject(vm, ((Integer)dvmObject.getValue()).toString());
}
新的問題+1
case "java/lang/StringBuffer->toString()Ljava/lang/String;": {
return new StringObject(vm, ((StringBuffer)dvmObject.getValue()).toString());
}
再次運(yùn)行
淚目封字,終于出結(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)的代碼
上面這些是unidbg的作者已經(jīng)實(shí)現(xiàn)的方法证薇,我們只需要在后面補(bǔ)上需要實(shí)現(xiàn)的方法即可。當(dāng)然匆篓,保證你的實(shí)現(xiàn)的方式具有通用性浑度,不然在逆向新的app的時候出了問題的話一時間很難察覺。
代碼僅供把玩鸦概。