App一般逆向流程记录

1.抓包

使用CharlesPostern,通过VPN代理形式进行抓包,而不是通过给WIFI设置HTTP代理的方式。使用VPN可以同时抓到Http(s)Socket的包。

2.抓取目标用户的用户信息

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

加密参数shawshank,打开jadx搜索shawshank,得知该参数是一个n2类里名为E的常量,接下来搜n2.E,调用的位置锁定位置。

com.hzhu.m.f.b.d.a

关键代码:

    public static String a(TreeMap<String, String> treeMap) {
        String replace = com.hzhu.base.f.x.a.a(com.hzhu.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();
    }

使用objection动态调试下

objection -g  com.hzhu.m explore

hook下上面的方法

android hooking watch class_method com.hzh
u.m.f.b.d.a --dump-args --dump-backtrace --dump-return

得到结果

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

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

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

android hooking watch class_method com.hzh
u.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.hzhu.m.f.b.d.a("<instance: java.util.TreeMap>")
(agent) [0475123810009] Return Value: "qEpcsu2CCkruqxB6h.itrY2p2tx1wchcSiAE5QNgxOMAtH4yGpq4n4C9P3JM9nDz4I23igrYVBNTsiY9eVP5NvV-bE3Su6aspx_z2xZfusGGtETbuehv2g="
(agent) [1408148254974] Called com.hzhu.m.f.b.d.a(boolean)
(agent) [1408148254974] Backtrace:
    com.hzhu.m.f.b.d.a(Native Method)
    com.hzhu.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.hzhu.m.f.b.c.intercept(HhzExceptionCheckInterceptor.kt:16)
    .....后面的调用栈省略

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

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

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

android hooking generate simple com.hzhu.m.f.b.d

结果

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

        //

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

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

frida -U com.hzhu.m -l crack_haohaozhu.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.r0ysue.gson.Gson').$new();

    var clazz = Java.use('com.hzhu.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"}

接下来就是找到算法的位置。将之前静态分析的代码简化

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

首先,

com.hzhu.base.f.x.b.a(new Gson().toJson((Object) treeMap), n2.B)

我们知道他的返回值为byte[] 类型,然后看外层的com.hzhu.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.hzhu.base.f.x.b.a一顿操作生成byte数组

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

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

以上就是本次分享的所有内容,如果你觉得文章还不错,欢迎关注公众号:Python编程学习圈,每日干货分享,发送“J”还可领取大量学习资料。或是前往编程学习网,了解更多编程技术知识。

### 使用 Frida 实现反调试技术 #### 反调试概述 在逆向工程过程中,目标程序通常会集成各种形式的防调试机制以阻止分析人员深入了解其运行逻辑。这些保护措施可能包括但不限于检测是否附加了调试器、检查进程状态以及监控特定事件的发生等[^1]。 #### 基于Frida实现反调试的具体方法 ##### 1. 阻断常见的调试接口调用 许多移动应用程序会在启动初期执行一系列用于确认环境安全性的验证过程。如果发现有外部工具试图干预,则立即终止自身或其他异常行为作为响应。为了绕过此类限制,可以采用拦截并修改敏感函数返回值的方式: ```javascript Interceptor.attach(Module.findExportByName(null, 'isDebuggerPresent'), { onEnter: function (args) { this.return_value = false; // 修改默认判断结果为未被调试 }, onLeave: function (retval) { retval.replace(this.return_value); // 替换原始输出 } }); ``` 此段脚本展示了怎样利用 `Interceptor` API 来挂钩 Windows 平台下的标准库函数 `IsDebuggerPresent()` ,从而欺骗目标认为当前不存在任何调试活动正在进行中[^4]。 ##### 2. 处理动态加载模块中的隐藏逻辑 对于那些采用了复杂加载策略的应用来说,仅仅依靠静态分析往往难以获得完整的代码路径覆盖度。此时借助像 Frida 这样的注入框架能够实现在内存层面实时跟踪新引入的对象及其关联的操作流。特别是针对经过高度优化甚至加密处理后的二进制文件而言,这种方法尤为有效[^3]。 例如,在面对 Flutter 应用时,由于其独特的编译方式使得传统的字节码解析变得不再适用;而通过上述手段则可以在不解密的前提下获取到更多关于内部组件交互的信息[^2]。 ##### 3. 绕过基于时间戳或计数器的时间差攻击防御 部分软件可能会记录下每次重要操作之间所耗费的实际秒数,并据此推算是否存在人为延缓现象——这通常是手动单步追踪留下的痕迹之一。对此类情况可以通过调整系统时钟读取函数的行为来达到目的: ```javascript var orig_gettime = new NativeFunction(Module.findBaseAddress('libc.so').add(0x7f8), 'int', ['pointer']); function fake_gettime(buffer){ var result = orig_gettime(buffer); Memory.writeUtime(buffer.add(Process.pointerSize), Date.now() / 1e3 | 0); // 设置固定的时间点 return result; } Interceptor.replace(orig_gettime.address, Interceptor.createCallback(fake_gettime)); ``` 这段 JavaScript 脚本实现了对 Linux 系统上常用的 `gettimeofday()` 函数替换,确保无论何时查询都能得到一致的结果,进而规避因时间间隔不正常引发的安全警报。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值