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狗


相关文章
|
1月前
|
JSON 监控 数据格式
1688 item_search_app 关键字搜索商品接口深度分析及 Python 实现
1688开放平台item_search_app接口专为移动端优化,支持关键词搜索、多维度筛选与排序,可获取商品详情及供应商信息,适用于货源采集、价格监控与竞品分析,助力采购决策。
|
1月前
|
缓存 监控 Android开发
京东 item_get_app 接口深度分析及 Python 实现
京东item_get_app接口可获取商品原始详情数据,包含更丰富的字段和细节,适用于电商分析、价格追踪等场景。需通过认证获取权限,支持字段筛选和区域化数据查询。
|
2月前
|
缓存 数据挖掘 API
淘宝 item_get_app 接口深度分析及 Python 实现
淘宝item_get_app接口是淘宝开放平台提供的移动端商品详情数据获取接口,相较PC端更贴近APP展示效果,支持获取APP专属价格、促销活动及详情页结构,适用于电商导购、比价工具、数据分析等场景。接口采用appkey+appsecret+session认证机制,需申请相应权限。本文提供Python调用示例及使用注意事项,帮助开发者高效对接移动端商品数据。
|
4月前
|
JavaScript
TypeOrmModule 从 app.module.ts 抽离到 database.module.ts 后出现错误的原因分析
本文分析了TypeORM实体元数据错误的成因,主要涉及实体注册方式、路径解析差异及模块结构变化导致的关系解析问题,并提供了具体解决方案和最佳实践建议。
132 56
|
1月前
|
缓存 供应链 开发者
1688 item_get_app 接口深度分析及 Python 实现
1688平台item_get_app接口专为移动端设计,提供商品原始详情数据,包含批发价格、起订量、供应商信息等B2B特有字段,适用于采购决策、供应链分析等场景。接口需通过appkey+access_token认证,并支持字段筛选,返回结构化数据,助力企业实现智能采购与供应商评估。
|
2月前
|
数据采集 数据可视化 API
驱动业务决策:基于Python的App用户行为分析与可视化方案
驱动业务决策:基于Python的App用户行为分析与可视化方案
|
6月前
|
数据采集 数据可视化 数据挖掘
基于Python的App流量大数据分析与可视化方案
基于Python的App流量大数据分析与可视化方案
|
11月前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
186 5

热门文章

最新文章