服务器做网站流程,手机网站开发者模式,租域名和服务器要多少钱,网站管理页面前言 在逆向过程中#xff0c;需要结合frida或unidbg#xff0c;对整个算法进行一步步的分析#xff0c;有时候我们分析完某一部分#xff0c;想要继续往下分析时#xff0c;需要重新发起请求#xff0c;这时候的参数往往都已经改变了#xff0c;这样会打断我们的节奏需要结合frida或unidbg对整个算法进行一步步的分析有时候我们分析完某一部分想要继续往下分析时需要重新发起请求这时候的参数往往都已经改变了这样会打断我们的节奏影响效率。此外有些算法除了我们外部传进去的参数外还有一些其他的参数参与了加密比如时间戳随机数一旦这些参与了算法那么即使每次的传入参数不变加密的结果还是会变。
外部输入参数的固定 frida 在京东app的hook中我们选择编写一个函数能够固定的调用Java层的native函数。
var bptr Module.findBaseAddress(libjdbitmapkit.so)
console.log(bptr);function hook_12ECC() {Interceptor.attach(bptr.add(0x12ECC1), {onEnter: function(args) {this.arg0 args[0];this.arg3 args[3];console.log(args[0], args[1], args[2], args[3], args[4]);dump(arg0-ptr0, args[0].readPointer());dump(arg0-ptr1, args[0]);dump(arg0-ptr1, args[0].add(4).readPointer(), 64);console.log(arg1, args[1].readCString());// dump(arg2, args[2]);console.log(arg3, args[3].readCString(parseInt(args[4])));},onLeave: function(){console.log(arg0-ret, this.arg0);dump(ret-arg0-ptr1, this.arg0);// dump(ret-arg0-ptr2, this.arg0.add(8).readPointer());dump(12ecc-ret-arg2, this.arg3)}})
}function callBitmapkitUtils() {var BitmapkitUtils Java.use(com.jingdong.common.utils.BitmapkitUtils);var currentApplication Java.use(android.app.ActivityThread).currentApplication();var context currentApplication.getApplicationContext();var b clientImage;var c {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}};var d d5a585639f505b18;var e android;var f 10.2.0;var res BitmapkitUtils.getSignFromJni(context, b, c, d, e, f);console.log(res: , res);
}Java.perform(function() {hook_12ECC();
})然后启动frida之后可以在shell中输入callBitmapkitUtils()来调用函数。
这样一来不像在手机上滑动页面、点击页面那样有时会有多个请求发出会多次调用加密的方法。这样的好处是再也不用在手机上操作了而且请求的内容和个数是可控的。不过我们注意到京东的参数里有st和sv这两个参数这可不是由我们传入的属于内部输入参数接下来我们要固定它们。
unidbg 对于unidbg而言在我们编写代码的时候一般都是固定输入的
内部输入参数的固定 frida 对于内部输入参数而言可能有时间戳随机数常量数字或字符其中前2个是会改变的这会影响逆向分析所以需要固定这两个参数。时间戳一般是调用libc.so的gettimeofday函数随机数则是调用libc.so的lrand48或srand48
function hook_libc(){var ptr_t Module.findExportByName(libc.so, gettimeofday);Interceptor.attach(ptr_t, {onEnter: function(args){this.arg0 args[0];},onLeave: function() {this.arg0.writeU32(1639393559);this.arg0.add(4).writeU32(0);}});Interceptor.attach(Module.findExportByName(libc.so , lrand48), {onEnter: function(args) {},onLeave:function(retval){retval.replace(1);}});
}
Java.perform(function() {hook_libc();hook_12ECC();
})这样一来即使多次运行st和sv也不会改变有利于我们的分析。
不过固定时间戳有个问题其他函数在获取时间戳的时候发现不对可能会导致frida环境崩溃所以我们希望只在调用的时候固定时间戳。所以我们更新如下代码
var logvar 0;function callBitmapkitUtils() {var BitmapkitUtils Java.use(com.jingdong.common.utils.BitmapkitUtils);var currentApplication Java.use(android.app.ActivityThread).currentApplication();var context currentApplication.getApplicationContext();var b clientImage;var c {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}};var d d5a585639f505b18;var e android;var f 10.2.0;logvar 1;var res BitmapkitUtils.getSignFromJni(context, b, c, d, e, f);logvar 0;console.log(res: , res);
}function hook_libc(){var ptr_t Module.findExportByName(libc.so, gettimeofday);Interceptor.attach(ptr_t, {onEnter: function(args){this.arg0 args[0];},onLeave: function() {if (logvar) {this.arg0.writeU32(1639393559);this.arg0.add(4).writeU32(0);}}});Interceptor.attach(Module.findExportByName(libc.so , lrand48), {onEnter: function(args) {},onLeave:function(retval){if (logvar){retval.replace(1);}}});
}这样一来只有在logvar值为1时才会固定参数。而logvar的默认值为0只有在调用callBitmapkitUtils方法的时候才会改为1调用完成后又会改为0。
unidbg 对于unidbg我们运行多次后会发现时间戳st会变sv一直是111好像和frida上的表现不一样难道出了什么问题
其实如果研究了京东libjdbitmapkit.so就会发现sv的后两位都是随机数余3。
而在unidbg对libc.so的随机数生成的实现中种子是固定的我猜的没深究源码导致生成的随机数的顺序是固定的继而导致余数是固定的。
public void hook_libc() {IHookZz hookZz HookZz.getInstance(emulator);hookZz.wrap(module.findSymbolByName(lrand48), new WrapCallbackHookZzArm32RegisterContext() {Overridepublic void preCall(Emulator? emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {}Overridepublic void postCall(Emulator? emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {int old ctx.getIntArg(0);System.out.println(Origin rand: old);ctx.setR0(1);}});hookZz.wrap(module.findSymbolByName(gettimeofday), new WrapCallbackHookZzArm32RegisterContext() {Overridepublic void preCall(Emulator? emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {Pointer pointer ctx.getR0Pointer();ctx.push(pointer);}Overridepublic void postCall(Emulator? emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {Pointer pointer ctx.pop();pointer.setLong(0, 1639388888);pointer.setLong(4, 0);}});
}
public static void main(String[] args) {JingDong test new JingDong();test.hook_libc();test.callSign();
}可以看到时间戳已经被固定下来了而打印出来的两个随机数他们的除以3的余数都为1这也说明了为什么固定参数之前sv的值一直是111。
unidbg完整代码
package com.jingdong;import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.hook.hookzz.*;
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 com.sun.jna.Pointer;
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();}Overridepublic 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);}Overridepublic 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);}Overridepublic 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);}Overridepublic 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);}Overridepublic 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);}Overridepublic 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);}Overridepublic 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 hook_libc() {IHookZz hookZz HookZz.getInstance(emulator);hookZz.wrap(module.findSymbolByName(lrand48), new WrapCallbackHookZzArm32RegisterContext() {Overridepublic void preCall(Emulator? emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {}Overridepublic void postCall(Emulator? emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {int old ctx.getIntArg(0);System.out.println(Origin rand: old);ctx.setR0(1);}});hookZz.wrap(module.findSymbolByName(gettimeofday), new WrapCallbackHookZzArm32RegisterContext() {Overridepublic void preCall(Emulator? emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {Pointer pointer ctx.getR0Pointer();ctx.push(pointer);}Overridepublic void postCall(Emulator? emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {Pointer pointer ctx.pop();pointer.setLong(0, 1639388888);pointer.setLong(4, 0);}});}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.hook_libc();test.callSign();}
}关于京东加密 其实京东app有3套加密方案会根据随机数不同来选择不同的方案而unidbg生成的随机数和京东cv的生成方案导致sv一直是固定的从而一直调用其中一套方案。我们在逆向的时候其实只需要逆向出其中一套方案即可那个简单选哪个如果刚好是调用的那套方案的话那还好。如果不是一时之间又不知道怎么处理的话就有点倒霉了。 我将这个解密方法封装成api了直接调用api可以解密很方便了 技术交流QQ 408737515