数字加固的,FART就能脱干净
抓包结果:
复制代码 隐藏代码POST /resource/m/user/login h2Host: capi.lkcoffee.comcontent-type: application/x-www-form-urlencodedcontent-length: 989accept-encoding: gzipcookie: uid=1b97928d-4f49-4608-a8cf-fe1c68c79ca31735372261909user-agent: okhttp/3.10.0sign=41353263181759105410239409212020611079&q=8pR_VhJIrWrz_tZtV6BCHViaW-VcsIem1HE6z7xNRYyVtK9SqWgrrgfb3iFkKIEGsfrSjR3dQhXSTLVGCRHEigsSIniYTUc1GbWaPMS1lOY9vZ2x3z0mK7gNza9lU9h8N-SzBmY1heejAwpKAaGGZXcpEjvXKst3PxekVny9uCmrt0fR9QtBPjVeKuG6m30HPQ_w5pNwRmJY2om_-ilphzED6B5rgds3pueiqbdGUeR4QY_YtsT6c4S-envoPapy_4N_UKjFQQNrX6W2k1uVUhOICA2exoewlwX8kardXpC1U9iXi60LOiH9l5UW7dEyD-U-mF1GTiD0Nm6yCw9ujurcONQ8EMqCWYNor05CgCVQbgsDLfVPq6jb4HhaYZhTMfJFtfFTjcUsGUDpvTbbwcErFNfpXCgpWMTpMLskGw-WQTSxNu2aR_Ksm1vRraRbZmKgp87DpaKLF7e2AfngErqxikJBS6MVCSP_1JAVAHR6_mmXNoIT4RE91bjUmrE7m6URIOXtanYGhad5kY00nTlZLMiP89hRZ-bWWt-pdSM6O3SVGMuQgY2HVPgF9N2jBFAHlExu_GhNfXV68g_qF6sHALbWHgdJ_KD17xvIcJHoiYbpEYk7I_GlIsZrf-F_9-ZHoaebmnPXCu__MeQuzm4yWsEukX3PE1-J2BW9Qpqtf1tQj9QU0yLO9oqBKk2UMWt6_ZNGMPQTi8uiqd_H15xb3NmhyOR5HmmOLd5AY9_4byqPXorZpwib32EhmV-C6KFYlrb4g_m0H9p6GoIAdOBHe4WFXhFs9TPr5zFiLQ8TrT5tdHk2R1tZmQyKovAsIPTi4-rxjQzCM5u0V9sWf65GI0YFnkwRU9PKWaulUQg%3D&uid=1b97928d-4f49-4608-a8cf-fe1c68c79ca31735372261909&cid=210101
这里是老版本的APP,没有抓包检测,直接进行协议逆向就是了。
关键代码函数定位:(这里是因为我已经抓取过才会直接判断key是sign和q的)
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
sign:
q:
这里的q是在isEmpty和hashMap都能得到堆栈信息,能够看到的是在sign的位置是在
复制代码 隐藏代码com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:14)com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:7)
一个在7行,一个在14行,我们去看看这里的函数不过两个都是通过hashMap来定位的
先来看q,因为sign值也需要传入q值。
复制代码 隐藏代码String b2 = c.b(com.alibaba.fastjson.a.toJSONString(map));
在这里可以看到加密函数了,至于是AESwork,还是AESwork4Api,其实能够判断到是后者,因为前面进行了base64的decode,不过可以去HOOK一下this.f26285e.a();的返回值,可以看看条件判断走的哪
最后发现是Native的函数,同时我们再去看看sign值的位置,看看函数往哪走
顺着 return c.f26578b.a(a(hashMap));
找到了md5_crypto,也就是另外一个native方法
这里的字符串是加密的,我们通过主动调用去查看一下是哪个so加载进行的native
复制代码 隐藏代码functiondecrypto_str(input){Java.perform(function(){varClass = Java.use('com.stub.StubApp');// 检查是否传入参数if (!input) {console.log('没有提供参数!');return; }var result = Class.getString2(input);console.log('调用结果: ' + result); });}
能够看到是ollvm的混淆,左下角也能看到对应的流程图,然后我们再去搜索了java_查看是不是静态注册的函数,结果发现并不是的
那就是动态注册的函数了,那我们要先去找到对应的动态注册的函数。
复制代码 隐藏代码varRegisterNativesarray = [];var symbols = Module.enumerateSymbolsSync("libart.so");for (var i = 0; i < symbols.length; i++) {var symbol = symbols[i];if (symbol.name.indexOf("art") >= 0 &&symbol.name.indexOf("JNI") >= 0 && symbol.name.indexOf("RegisterNatives") >= 0 ) {RegisterNativesarray.push(symbol.address);console.log("RegisterNatives is at ", symbol.address, symbol.name);continue; }}
这里去获取了一个可能为函数注册的函数数组
是有两个的,那么我们去遍历这两个函数,看看是不是有对应去动态注册的函数
复制代码 隐藏代码jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
这里有些代码是从如画那里改的,我为了确定要是在哪个动态注册的函数,所有使用闭包确保当前的 i 被正确捕获
复制代码 隐藏代码structJNINativeMethod {constchar* name; // 0: 方法的名称(指向 C 字符串)constchar* signature; // 1: 方法的签名(指向 C 字符串)void* fnPtr; // 2: 本地方法的实现函数指针(指向 C 函数)};
这里获取到的 JNINativeMethod 是结构体,所以在获取指针的时候是这样写的
复制代码 隐藏代码for (var j = 0; j < method_count; j++) { // 这里使用另一个 i 作为方法索引var name_ptr = Memory.readPointer(methods_ptr.add(j * Process.pointerSize * 3));var sig_ptr = Memory.readPointer(methods_ptr.add(j * Process.pointerSize * 3 + Process.pointerSize));var fnPtr_ptr = Memory.readPointer(methods_ptr.add(j * Process.pointerSize * 3 + Process.pointerSize * 2));
复制代码 隐藏代码if (RegisterNativesarray.length > 0) {for (let i = 0; i < RegisterNativesarray.length; i++) {// 使用闭包确保当前的 i 被正确捕获 (function(i) {Interceptor.attach(RegisterNativesarray[i], {onEnter: function (args) {console.log("come to addrRegisterNatives[" + i + "]"); // 输出正确的 ivar env = args[0]; // jni对象var java_class = args[1]; // 类var class_name = Java.vm.tryGetEnv().getClassName(java_class);var taget_class = "com.luckincoffee.safeboxlib.CryptoHelper"; // 目标类名if (class_name === taget_class) {console.log("n[RegisterNatives] method_count:", args[3]);var methods_ptr = ptr(args[2]);var method_count = parseInt(args[3]);for (var j = 0; j < method_count; j++) { // 这里使用另一个 i 作为方法索引var name_ptr = Memory.readPointer(methods_ptr.add(j * Process.pointerSize * 3));var sig_ptr = Memory.readPointer(methods_ptr.add(j * Process.pointerSize * 3 + Process.pointerSize));var fnPtr_ptr = Memory.readPointer(methods_ptr.add(j * Process.pointerSize * 3 + Process.pointerSize * 2));var name = Memory.readCString(name_ptr);var sig = Memory.readCString(sig_ptr);var find_module = Process.findModuleByAddress(fnPtr_ptr);var offset = ptr(fnPtr_ptr).sub(find_module.base);console.log('class_name:', class_name, "name:", name, "sig:", sig, 'module_name:', find_module.name, "offset:", offset); } } } }); })(i); // 在此传入当前的 i }}
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
0
这里我们通过了找到的这个动调注册函数的函数,并且对于这个函数进行了HOOK,在每一次进行函数的动态注册时就直接去打印对于的注册的函数以及对应的偏移地址,然后我们就去实现unidbg的算法复现。
unidbg环境
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
1
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
2
我们这里使用new AndroidModule(emulator, vm).register(memory);直接去虚拟出来这个需要的API就可以了
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
3
这里我们去尝试着去调用(首先是确定传入的参数是什么)刚刚在动态加密的加密函数localAESWork4Api,不过这里是通过的动态注册的方式来实现的函数注册,所以不能直接去通过静态注册的方式直接实现
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
4
那么这里就去主动调用这个加密函数了,看看我们加密的之前和之后的结果是什么。
这里我们调用的这个函数是在我们HOOK动态注册函数时得到的结果,同时在函数加密结束之后再进行HOOK
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
5
在aes128_enc_wb_coff函数中,我们可以看到很多查表法的异或操作,其实就已经很像是aes的内部算法了,同时,我们要去实现密钥的破译,其实是需要进行DFA攻击的,那么就需要就找到故障注入的位置,所以要去明确加密过程
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
6
这里给出了部分的测试结果
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
7
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
8
复制代码 隐藏代码functionget_maincode() {functionshowstack(){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() ) ); }Java.perform(function(){var textUtils = Java.use("android.text.TextUtils") textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) {showstack();console.log("textUtils.isEmpty arg:.implementation:" + a);returnthis.isEmpty(a); };var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 hashMap.put.implementation = function(key, value) {console.log("hashMap.put key: " + key + " value: " + value);if(key.equals("sign")|| key.equals("q")){showstack(); }returnthis.put(key, value); } });}
9
sign的分析:
复制代码 隐藏代码com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:14)com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:7)
0
这样是的定位也是通过动态注册找的
首先是在208的位置找到了MD5的位置
复制代码 隐藏代码com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:14)com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:7)
1
复制代码 隐藏代码com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:14)com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:7)
2
拿着这串新的数据去MD5,发现也不是正确的结果,那么就说明是有问题的。
复制代码 隐藏代码com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:14)com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:7)
3
在函数结束之后去查看encrypt digest encryptoText addr的地址
用python复现一下:
复制代码 隐藏代码com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:14)com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:7)
4
· 今 日 推 荐 ·
本文内容来自网络,如有侵权请联系删除
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...