App逆向百例|13|某段子App参数分析

简介: App逆向百例|13|某段子App参数分析

观前提示:

本文章仅供学习交流,切勿用于非法通途,如有侵犯贵司请及时联系删除

样本:aHR0cHM6Ly9wYW4uYmFpZHUuY29tL3MvMXl5N2Rtd1YtcEEtS1B3WmUtN3FRNlE/cHdkPWxpbm4=

0x1 抓包&定位

打开App可以直接抓包

可以看到链接的sign以及request dataresponse data都被加密需要分析

直接jadx-gui搜索sign=结果不多

跳转过来 还不清楚传入内容是什么 查找用例看调用处

从这里可知request data是AES加密后的密文 sign是对AES加密后的密文进行一个签名

从代码可知 具体sign加密调用的是net_crypto.so的一个native方法

同样的实现AES加解密的是encodeAESdecodeAES

并且可知 在加载so的时候调用了一次初始化方法

0x2 Unidbg黑盒调用

简简单单造轮子 造完直接运行 目前还未有需要补的环境

加载完后 需要主动调用native_init方法

public void native_init() {
    ArrayList<Object> args = new ArrayList<>(3);
    args.add(vm.getJNIEnv());
    args.add(0);
    module.callFunction(emulator, 0x4a069, args.toArray());
}

开始报错 提示缺少一些环境

根据报错把缺少环境补上即可

vm.resolveClass("android/content/Context").newObject(null)

补完这个初始化方法就不报错了

接着主动调用sign方法 这里图方便 直接将第二个传入参数改为了123456 只追求算法

public String sign() {
        ArrayList<Object> args = new ArrayList<>(3);
        args.add(vm.getJNIEnv());
        args.add(0);
        String str = "http://api.xxxxx.com/account/login";
        args.add(vm.addLocalObject(new StringObject(vm, str)));
        ByteArray byteArray = new ByteArray(vm, "123456".getBytes(StandardCharsets.UTF_8));
        args.add(vm.addLocalObject(byteArray));
        Number number = module.callFunction(emulator, 0x4a28d, args.toArray());
        String result = vm.getObject(number.intValue()).getValue().toString();
        return result;
    }

运行看报错接着补环境

case "android/content/Context->getClass()Ljava/lang/Class;": {
    return dvmObject.getObjectType();
}

case "java/lang/Class->getSimpleName()Ljava/lang/String;": {
    return new StringObject(vm, "AppController");
}

case "android/content/Context->getFilesDir()Ljava/io/File;": {
    return new StringObject(vm, "/data/user/0/cn.xxxx.tieba/files");
}

case "java/lang/String->getAbsolutePath()Ljava/lang/String;": {
    return new StringObject(vm, "/data/user/0/cn.xxxx.tieba/files");
}

case "android/os/Debug->isDebuggerConnected()Z": {
  return false;
}

case "android/os/Process->myPid()I": {
    return emulator.getPid();
}

补到这里 成功出值 可以为所欲为了

算法分析

sign

根据Unidbg输出的动态注册偏移跳转到0x4a28d

看到方法很多 不知道都做了些什么

回到Unidbg 根据JNI输出可以得到很多信息

这里取了一些基本信息 又进行了MD5 那可以猜测这里可能是进行签名验证

这里是对运行环境的一个检测 前面补的环境就是Pass掉这些检测

往下看 这里就出现了加密的结果 根据偏移跳转过来

所以先把目光放在sub_4A9D8(v15, v17)

进入sub_4A9D8->sub_4AA00

sub_4AAB4就是申请内存

由于样本被ollvm混淆过 所以变得罗里吧嗦

整段下来其实就只是一个内存拷贝

回到上层 现在关注的就是v17

0x4A9D8处下个console断点

根据代码可知 这里的a2需要再偏移

这里已经存在有结果 所以还得继续找

忽视那些检测代码 剩下的代码并不多

sub_4B458就是做一个拼接操作

byte_1C91B0对应的就是v2-

最后猜测具体的算法就在sub_655DC

进入sub_655DC 只有俩方法 先看sub_65540

这熟悉的数组 实锤MD5

但是 仔细观察 这个md5的魔数和我们普通的貌似不一样

确实不一样 实锤了魔改 先尝试拿MD5修改看看能不能对应得上

和主动调用的对比 确定了仅仅是改了魔数

在后在拼接上v2-就完成这一个sign的加密

encodeAES

先主动调用 方便后面调试

public byte[] encodeAES() {
    ArrayList<Object> args = new ArrayList<>(3);
    args.add(vm.getJNIEnv());
    args.add(0);
    String str = "123456";
    args.add(vm.addLocalObject(new ByteArray(vm, str.getBytes(StandardCharsets.UTF_8))));
    Number number = module.callFunction(emulator, 0x4a0b9, args.toArray());
    Inspector.inspect((byte[]) vm.getObject(number.intValue()).getValue(), "AESencode");
    return (byte[]) vm.getObject(number.intValue()).getValue();
}

接着根据偏移跳转0x4a0b9

sub_67178作用为GetByteArrayElements

进入sub_5E0E0->sub_5E0F8

看到这是一个标准的AES\CBC\128加密 那么接下来的目标就是找出KEY和IV

IV生成位置在sub_5D090

进入sub_5D090发现就是一个随机方法

拿到随机IV后 根据传入明文的长度进行计算出一个校验位 放在第2位中

根据hook结果可得到验证

并且根据输出结果可知 随机生成的iv拼接在了密文的前面传回服务器

接下来就是KEY 回到上层看到a2存在dword_1CC3F0

拿到地址traceWrite

然后跳转偏移地址 跳转过来是一个内存拷贝操作

还得继续回到上层找

sub_5D090

所以KEY和IV一样是随机生成的 那么问题来了 随机生成的KEY怎么能让服务器知道呢 不像IV能拼接在密文前面

其实在native_init的时候就生成了随机的KEY 并且每次打开App都只生成一次后不变动

那猜测一定是在打开App的时候将KEY上传了

打开抓包 并重新打开App 通过观察 可以看到频繁出现了X-Xc-Proto-ReqX-Xc-Proto-Res

X-Xc-Proto-Req

X-Xc-Proto-Res

最终还得回到so分析

主动调用getProtocolKey

public void getProtocolKey() {
    ArrayList<Object> args = new ArrayList<>(2);
    args.add(vm.getJNIEnv());
    args.add(0);
    Number number = module.callFunction(emulator, 0x4a419, args.toArray());
    System.out.println(vm.getObject(number.intValue()).getValue().toString());
}

发现在native_init后就生成了X-Xc-Proto-Req 所以应该是在初始化的时候就生成了KEY顺便也生成了X-Xc-Proto-Req

进入sub_4A068->sub_5CD9C

进入sub_5D4D8 可以发现这是一个SHA256withRSA

asc_1C9E70为公钥

将随机的KEY进行加密后作为X-Xc-Proto-Req上传后即为成功上传KEY 然后会返回X-Xc-Proto-Res

在下次请求时作为标识带上即可

decodeAES

如名字所示 就是AES解密 KEY不变 IV为返回内容的前16位

自己实现AES解密后发现 返回的内容还是乱码 转为HEX时发现1F 8B 08字眼

这是GZIP的头部标识

此时 只需要再将解密出来的内容再进行GZIP解密即可

在Python中实现




感谢各位大佬观看

感谢大佬们的文章分享

如有错误 还请海涵

共同进步 带带弟弟


点赞 在看 分享是你对我最大的支持

逆向lin狗


相关文章
|
4月前
|
存储 XML Android开发
Android Studio App开发入门之数据存储中共享参数SharedPreferneces的讲解及使用(附源码 超详细必看)
Android Studio App开发入门之数据存储中共享参数SharedPreferneces的讲解及使用(附源码 超详细必看)
31 0
|
25天前
|
数据采集 小程序 网络安全
云擎技术---分析工信部APP备案的“传闻”
APP备案并非新事物,自2005年起已有非经营性互联网信息服务备案制度。备案针对的是网站主办者,而非用户,不涉及个人用户网络访问。网络接入服务提供者包括ISP和IDC,不限于三大运营商。通知要求不为未备案网站提供接入,但不影响国外软件使用。个人开发者不能涉及经营性内容,备案审核时长1-20个工作日。境内服务器和国内应用商店需备案,境外则无需。手机厂商不会开启白名单制,仅实行黑名单制。APP备案与民营经济发展壮大意见不冲突,工信部有权颁布相关规定。该政策不存在逐步试探底线情况,所有解读均有法律依据。
26 3
云擎技术---分析工信部APP备案的“传闻”
|
2月前
|
网络协议 算法 Android开发
安卓逆向 -- 实战某峰窝APP(动态分析)
安卓逆向 -- 实战某峰窝APP(动态分析)
34 4
|
2月前
|
算法
某圈app算法分析
某圈app算法分析
18 0
|
2月前
|
算法 安全 数据安全/隐私保护
某影视APP算法逆向分析
某影视APP算法逆向分析
19 0
|
2月前
|
算法 Java
某江app算法分析
某江app算法分析
17 0
|
2月前
|
算法 数据挖掘 数据安全/隐私保护
某合伙人app算法分析
某合伙人app算法分析
15 0
|
4月前
|
数据可视化 数据挖掘
【数据分析与可视化】使用pyecharts对App下载量数据进行可视化分析(附源码)
【数据分析与可视化】使用pyecharts对App下载量数据进行可视化分析(附源码)
48 0
|
6月前
|
中间件 API
关于 Express API app.use 中的 path 参数用法
关于 Express API app.use 中的 path 参数用法
37 0
关于 Express API app.use 中的 path 参数用法
|
7月前
|
算法 数据安全/隐私保护
App逆向百例|17|某音乐App分析
App逆向百例|17|某音乐App分析
159 0