某A系电商App doCommandNative浅析

一、目标

李老板: 奋飞呀,x-sign你都水了好几篇了,一直在Apk里面打转,咱们啥时候分析分析它的so?

奋飞: 循序渐进嘛,我们上次刚定位了它的so,今天我们来分析分析。

App版本: v4.15.1

二、步骤

Native层的入口

先回忆下这个堆栈

1[NewStringUTF] bytes:x-sign 2Rc Full call stack:dalvik.system.VMStack.getThreadStackTrace(Native Method) 3 tt: java.lang.Thread.getStackTrace(Thread.java:1538) 4 tt: com.txxxao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method) 5 tt: com.axxbxxx.wireless.security.mainplugin.а.doCommand(Unknown Source:0) 6 tt: com.axxbxxx.wireless.security.middletierplugin.c.d.a.a(Unknown Source:280) 7 tt: com.axxbxxx.wireless.security.middletierplugin.c.d.a$a.invoke(Unknown Source:56) 8 tt: java.lang.reflect.Proxy.invoke(Proxy.java:913) 9 tt: $Proxy12.getSecurityFactors(Unknown Source) 10 tt: mtopsdk.security.d.a(lt:620) 11 tt: mtopsdk.mtop.a.a.a.a.a(lt:218) 12 tt: mtopsdk.framework.a.b.d.b(lt:45) 13 tt: mtopsdk.framework.b.a.a.a(lt:60) 14 150xcb434e10 libsgmiddletierso-6.5.50.so!0x33e10 160xcb404e28 libsgmiddletierso-6.5.50.so!0x3e28 170xc9dd5536 libsgmainso-6.5.49.so!0x10536 180xc9dd71c8 libsgmainso-6.5.49.so!0x121c8 190xf365607a libart.so!art_quick_generic_jni_trampoline+0x29 200xf364068a libart.so!MterpAddHotnessBatch+0x29 210xf3651b76 libart.so!art_quick_invoke_stub_internal+0x45

堆栈会说话的,他告诉我们

1、jni函数叫 com.txxxao.wireless.security.adapter.JNICLibrary.doCommandNative。

2、doCommandNative的实现在libsgmainso-6.5.49.so中,可能在偏移 0x121c8 附近。

先Hook jni函数一把

jni函数会告诉我们入参和返回值的类型,所以不能放过。

这个jni函数的声明在 libsgmain.so 这个假so里面

docmd.png

这个jni函数有两个参数,第一个参数是int型,第二个参数是Object数组

我们先上frida看看它是不是我们的目标。

1Java.enumerateClassLoaders({ 2 "onMatch": function(loader) { 3 if (loader.toString().indexOf("libsgmain.so") >= 0 ) { 4 Java.classFactory.loader = loader; // 将当前class factory中的loader指定为我们需要的 5 console.log("loader = ",loader.toString()); 6 7 } 8 }, 9 "onComplete": function() { 10 console.log("success"); 11 } 12}); 13 14// 此处需要使用 Java.classFactory.use 15var signCls = Java.classFactory.use('com.txxxao.wireless.security.adapter.JNICLibrary'); 16signCls.doCommandNative.implementation = function(a,b){ 17 var retval = this.doCommandNative(a,b); 18 console.log(" #### >>> a = " + a); 19 20 if( a == 70102){ 21 console.log(" #### >>> Obj = " + b); 22 } 23 24 console.log(" #### >>> rc= " + retval) // .entrySet().toArray()); 25 26 // var stack = threadinstance.currentThread().getStackTrace(); 27 // console.log("#### >>> Rc Full call stack:" + Where(stack)); 28 29 return retval; 30} 31// */

这里先解释下这个 70102 的来历,doCommandNative明显承担了很多功能,我们全部打印出来太乱了。

从之前的堆栈 com.axxbxxx.wireless.security.middletierplugin.c.d.a.a 这个类里面知道做 x-sign 签名的时候使用的命令参数是 70102 (对应的代码在 libsgmiddletier.so 这个假so里面)

跑一下

frc1.png

确认过眼神,就是它了。

TIP: Frida spawn模式跑这个脚本的时候, loader 没有输出, 这时候把脚本任意修改个空格,然后再保存。Frida会重新自动载入,这时候才能 有输出。

ida一下 libsgmainso-6.5.49.so

这个so还是很有料的。

首先他的导出函数里面找不到 doCommandNative 说明它是动态注册的。

ida1.png

其次是so里面稍微有点身份的函数都是这种动态跳转。有效的抵抗了ida的F5。

ida2.png

我们一个一个来解决。

动态注册我们不怕,Hook RegisterNatives就可以搞定它

[RegisterNatives] java_class: com.txxxao.wireless.security.adapter.JNICLibrary name: doCommandNative sig: (I[Ljava/lang/Object;)Ljava/lang/Object; fnPtr: 0x7637c25ba4 module_name: libsgmainso-6.5.49.so module_base: 0x7637c07000 offset: 0x1eba4

结果出来,我们的目标是 0x1eba4

比较尴尬的是,ida中的 0x1eba4 的位置怎么看都不像函数的样子。

怎么办?

从这个so的种种表现来看,会不会它在运行时有些自修改之类的玩法?

先不管那么多了,我们把这个so从运行时dump出来再说。

TIP: dump so的方法参考 http://91fans.com.cn/post/carcommunitytwo/

我的测试手机是 64位的,所以dump出来了一个 64位的so

idasub.png

这次有那么点意思了,不过由于烦人的 BR X11 动态跳转,导致我们还是不能愉快的f5

修复一下

如果我们知道这个 BR X11 指令的 x11的值,然后把它改成一个静态跳转,是不是可以修复可怜的F5?

说干就干

1var mbase = Module.getBaseAddress('libsgmainso-6.5.49.so'); 2Interceptor.attach(mbase.add(0x1EC18),{ 3 onEnter:function(args){ 4 console.log('Context : ' + JSON.stringify(this.context)); 5 } 6});

打印出来

Context  : {"pc":"0x7637921c18","sp":"0x7639089340","x0":"0x20","x1":"0x76390893e4","x2":"0x2776","x3":"0x28","x4":"0x1","x5":"0x0","x6":"0x4","x7":"0x0","x8":"0x16","x9":"0x7639089350","x10":"0x7637a6cd60","x11":"0x7637921c2c","x12":"0x76390893e8","x13":"0x76390893d8","x14":"0x1","x15":"0x0","x16":"0x76dadbf000","x17":"0x76da67d440","x18":"0x0","x19":"0x76506125e0","x20":"0x0","x21":"0x2776","x22":"0x76390896bc","x23":"0x7650261ddf","x24":"0x8","x25":"0x196","x26":"0x763908d588","x27":"0x2","x28":"0x76390893e8","fp":"0x76390893b0","lr":"0x76dadbf60c"}

当前地址是 0x7637921c18 - 0x1EC18 = 0x763793000,说明so基地址是 0x763793000 , x11的值是 0x7637921c2c - 0x763793000 = 0x1EC2C, 说明这里要跳转到 0x1EC2C

那就把这行指令修改成 b 0x1EC2C

idabx.png

再F5一下,就比原来漂亮一些了

idasubok.png

Hook 这个Native层的doCommandNative

这里主要就是介绍下 Hook Native函数的时候,如何打印 Object[] 类型的参数

1var mbase = Module.getBaseAddress('libsgmainso-6.5.49.so'); 2// 1ed4c 3Interceptor.attach(mbase.add(0x1EBA4),{ 4 onEnter:function(args){ 5 console.log('doCommandNative = ' + args[2].toString(10)); 6 7 8 var Object_javaArray = Java.use('[Ljava.lang.Object;'); 9 var ArrayArgs_3 = Java.cast(args[3], Object_javaArray); 10 var ArrayClz = Java.use("java.lang.reflect.Array"); 11 var len = ArrayClz.getLength(ArrayArgs_3); 12 13 if( args[2].toString(10) == 70102) { 14 for(let i=0;i!=len;i++){ 15 var objUse = ArrayClz.get(ArrayArgs_3,i); 16 if(objUse != null){ 17 console.log("args[3] String value:", objUse.toString()); 18 } 19 } 20 } 21 22 } 23});

先用 Java.cast 转一下类型,然后再 java.lang.reflect.Array 来遍历。

结果还是比较漂亮的

rc.png

三、总结

Native层的保护措施更多,大家都太卷了。

熟练掌握java的反射用法,是玩好frida的必要条件。

ida的F5也是大家严防死守的,所以修复的方案也要了解下。

ffshow.png 本想游戏人间,为何最后反被人间游戏。

TIP: 本文的目的只有一个就是学习更多的逆向技巧和思路,如果有人利用本文技术去进行非法商业获取利益带来的法律责任都是操作者自己承担,和本文以及作者没关系,本文涉及到的代码项目可以去 奋飞的朋友们 知识星球自取,欢迎加入知识星球一起学习探讨技术。有问题可以加我wx: fenfei331 讨论下。

关注微信公众号: 奋飞安全,最新技术干货实时推送

点赞
收藏

评论区

加载中...

相关推荐

小程序逆向分析 (一)

一、目标李老板:奋飞呀,最近耍小程序的比较多,而且貌似js好耍一点?要不咱们也试试?奋飞:你是老板,你说了算喽。第一次搞小程序,得找个软柿子捏,就找个以前分析过的某段子App的小程序吧。反编译静态分析动态调试二、步骤春天在哪里?app下载回来就是apk包,那么小程序在哪里?小程序是一个以wxapkg为后缀的文件,在android手机的/da

某A系电商App x-sign签名分析

一、目标前不久(我去,都大半年了)分析过我们找到了几个伪装成so的jar包。虽然rpc调用ok了,但是它的实际运算过程还是在so里面的。今天我们从它们同族的App来入手,利用Native层字符串定位的方式来定位下在哪个so中去做的运算。App版本:v4.15.1二、步骤特征字符串定位一开始选择的特征字符串是x,后来发现没有xsign好使In

某汽车社区App 签名和加解密分析 (二) : Frida Dump so

一、目标App安全的主战场在Native层,分析Native层的so,最趁手的兵器就是Frida和Unidbg了。今天我们的目标是某汽车社区Appv8.0.1so的分析。二、步骤特征字符串定位我们在上一篇教程已经定位了,数据加密和解密函数再java层的位置。按照常理来说,这个java类文件中,应该有个System.loadLibrary("

不能Hook的人生不值得 jsHook和模拟执行

一、目标李老板:奋飞呀,上次分析的那个App光能Debug还不够呀,网页中的js也用不了Frida,我还想Hook它的函数,咋搞呀?再有App可以RPC去执行签名,这个js我如何去利用呀?总不能代码都改成js去做请求吧?奋飞:老板呀,你一下提这么多要求,不是明摆着要我们加班吗?这次加班费可得加倍。二、步骤最简单易行的jsHookcon

某神奇App data加密算法解析(一)

一、目标李老板:奋飞呀,我遇到一个超级牛掰的App,它请求的时候有个data参数加密,用尽了你介绍的所有的方法,都找不到它是如何加密的。奋飞:子曾经曰过,老板的嘴,骗人的鬼。有这么牛掰的App,那么我们这帮兄弟早就失业了。某神奇Appv10.1.0点社区随便打开一篇有评论的文章今天的目标就是这个data二、步骤搜索特征字符串目标是data,所以我

手把手教你从Apk中取出算法

一、目标李老板:奋飞呀,我最近从Apk里面跟踪到一个算法,代码清晰,但是我不会java,把他翻译成python貌似挺费劲的,有没有轻松省力的方法呀?奋飞:有的呀,给我加工资,我来翻译。某电商Appv10.4.5,升级之后老有小伙伴说他的sign算法变了,其实他就是做了点小动作。sign参数没有动,uuid是明文去做签名,但是抓包请求里面找不到明文uu