app逆向java层一般步骤

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

经常听到小伙伴儿说在做爬虫的时候,尤其在app逆向的时候,对于如何定位参数一脸懵逼,大部分只知道使用jadx去搜索,搜不到了就靠直觉了,这样就比较的耗时,那么这里我就分享一下我在做逆向的时候的基本步骤,希望能够帮助大家。(为了规避法律风险对app信息做了脱敏处理。

截屏2021-10-13 上午11.33.06.png

0x0.逆向的工具以及版本的选择

这里我使用的手机的root了的pixel,不建议使用模拟器,因为很多app会检测是否使用了模拟器。 接下来是frida版本的选择,因为我的手机系统是安卓8的,根据大家的反馈,得知使用frida的12版本比较稳定,其他的容易出现假死的情况。 关于Frida的安装参照我之前的文章。juejin.cn/post/701838…

frida版本号14.2.2
objection==1.9.6
app版本4.22.0
复制代码

软件下载地址:aHR0cHM6Ly93d3cud2FuZG91amlhLmNvbS9hcHBzLzY3NTY5MjIvaGlzdG9yeV92NDAwMjIwMDE=(一种编码)

0x1.抓包

据我了解,目前大家常见的抓包方式是Charles,然后通过给WIFI设置HTTP代理的方式。因为Http层的抓包只能到应用层,而且会出现经常抓不到包的情况,所以这不推荐大家使用这种方式。 我们使用VPN作为代理的方式,可以同时抓到Http(s)Socket的包,且不管其来自Java层还是so层。手机上安装Postern,然后开VPN服务通过连接到开启Socks5服务端的抓包软件,将流量导出去。

对于Http的抓包,只要在电脑的Charles上配置好Socks5服务器,手机上用Postern开启VPN连上电脑上的CharlesSocks5服务器,所有流量即可导出到Charles上。另外我们也可以使用其他基于VPN抓包的软件,比如比较有名小黄鸟,HttpCanary可以直接在手机上就能抓包,无需配置电脑端。

通过抓包我们得到一个目标链接,它可以抓取目标用户的用户信息。

URLhttps://yapi.xxxx.cn/member/getUserInfo

0x2 Jadx静态分析

通过观察上面的请求得知,有个加密参数shawshank需要我们去破解,打开jadx搜索shawshank,可以得知该参数是一个n2类里名为E的常量,接下来搜n2.E,调用的位置锁定位置。

com.xxx.m.f.b.d.a

关健代码:

    public static String a(TreeMap<String, String> treeMap) {
        String replace = com.xxx.base.f.x.a.a(com.xxx.base.f.x.b.a(new Gson().toJson((Object) treeMap), n2.B)).replace("+", com.xiaomi.mipush.sdk.Constants.ACCEPT_TIME_SEPARATOR_SERVER).replace(com.appsflyer.share.Constants.URL_PATH_DELIMITER, "_");
        StringBuilder sb = new StringBuilder();
        sb.append(n2.A);
        sb.append(replace);
        return sb.toString();
    }

复制代码

0x3 objection动态调试

使用objection动态调试下

objection -g  com.xxx.m explore
复制代码

hook下上面的方法

android hooking watch class_method com.xxx
.m.f.b.d.a --dump-args --dump-backtrace --dump-return
复制代码

得到结果

(agent) Attempting to watch class com.xxx.m.f.b.d and method a.
(agent) Hooking com.xxx.m.f.b.d.a(java.util.TreeMap)
(agent) Hooking com.xxx.m.f.b.d.a(boolean)
(agent) Registering job 1408148254974. Type: watch-method for: com.xxx.m.f.b.d.a
复制代码

由结果得知他有两个重载方法。

因为通过jadx分析我们知道参数java.util.TreeMap的方法才是我们想要的。所以修改objection代码,在需要hook的方法后面加个空格加上参数类型,可以进一步锁定具体是那个hook方法的。

android hooking watch class_method com.xxx.m.f.b.d.a java.util.TreeMap --dump-args --dump-backtrace --dump-return
复制代码

--dump-args:打印参数

--dump-backtrace: 打印调用栈

--dump-return:打印返回值

然后现在objection处于等待状态,重新点击app上的内容,可以得到下面的内容,其中关键代码

(agent) [0475123810009] Arguments com.xxx.m.f.b.d.a("<instance: java.util.TreeMap>")
(agent) [0475123810009] Return Value: "qEpcsu2CCkruqxB6h.itrY2p2tx1wchcSiAE5QNgxOMAtH4yGpq4n4C9P3JM9nDz4I23igrYVBNTsiY9eVP5NvV-bE3Su6aspx_z2xZfusGGtETbuehv2g="
(agent) [1408148254974] Called com.xxx.m.f.b.d.a(boolean)
(agent) [1408148254974] Backtrace:
	com.xxx.m.f.b.d.a(Native Method)
	com.xxx.m.f.b.d$b.intercept(HttpInit.java:3)
	okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:10)
	okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:1)
	com.xxx.m.f.b.c.intercept(HhzExceptionCheckInterceptor.kt:16)
	.....后面的调用栈省略

复制代码

通过上面的结果 我们知道了返回值

qEpcsu2CCkruqxB6h.itrY2p2tx1wchcSiAE5QNgxOMAtH4yGpq4n4C9P3JM9nDz4I23igrYVBNTsiY9eVP5NvV-bE3Su6aspx_z2xZfusGGtETbuehv2g=就是我们的目标结果,然后参数就不知道是个啥了,只知道是java.util.TreeMap实例, objection对于这种复杂的参数类型是无法打印的,这个时候我们就要借助frida了

。先用objection生成个frida代码模版。

android hooking generate simple com.xxx.m.f.b.d
复制代码

结果

Java.perform(function() {
    var clazz = Java.use('com.xxx.m.f.b.d');
    clazz.a.implementation = function() {

        //

        return clazz.a.apply(this, arguments);
    }
});

复制代码

objection只能生成一个大概的框架代码,具体内容还需要自己加,保存下来先运行看看。之后执行代码

frida -U com.xxx.m -l crack_xxx.js
复制代码

得到错误提示,我们可以根据错误提示进一步优化代码,这里我们知道了a有两个重载方法。

Error: a(): has more than one overload, use .overload(<signature>) to choose from:
	.overload('java.util.TreeMap')
	.overload('boolean')
复制代码
Java.perform(function() {
    const gson = Java.use('com.google.gson.Gson').$new();

    var clazz = Java.use('com.xxx.m.f.b.d');
    clazz.a.overload('java.util.TreeMap').implementation = function(x) {
        //
        console.log("x:=",x);
        const json_x=gson.toJson(x)
        console.log("json_x",json_x);
        return clazz.a.apply(this, arguments);
    }
});
复制代码

得到传入的参数为

{"uid":"3171385"}
复制代码

接下来就是找到算法的位置。

0x4.动静结合分析

将之前静态分析的代码简化

  String replace = com.xxx.base.f.x.a.a(com.xxx.base.f.x.b.a(new Gson().toJson((Object) treeMap), null)).replace("+", "-").replace("/", "_");;
复制代码

首先,

com.xxx.base.f.x.b.a(new Gson().toJson((Object) treeMap), n2.B)
复制代码

我们知道他的返回值为byte[] 类型,然后看外层的com.xxx.base.f.x.a.a

根据jadx上面的提示

/* compiled from: Base64 */
复制代码

盲猜之后做了一个base64操作(后来证实确实是)。

得到结果replace之后还没完,接下来还有个

     StringBuilder sb = new StringBuilder();
        sb.append(n2.A);
        sb.append(replace);
        return sb.toString();
复制代码

查看代码得知n2.A是qEpcsu2CCkruqxB6h.itrY2p2tx。

至此到这里就分析完了。

最终的shawshank的结果就是上面的sb.toString()的值。

简单捋一下结果

1.传入目标字符串{"uid":"3171385"},注意是字符串。uid就是用户的id

2.com.xxx.base.f.x.b.a一顿操作生成byte数组

3.然后做个base64操作,将字节数组转成字符串。

4.在得到的结果前面拼接上n2.A,得到最终加密参数。

总结

在做app逆向的时候,一般大部分简单的app通过静态分析,搜索关键词就可以找到加密位置,但是对于复杂的这个时候,我们可以通过动静结合的方式进行快速的分析app。 原文来自我的博客园:www.cnblogs.com/c-x-a/p/151…

猜你喜欢

转载自juejin.im/post/7018386122262708232