写点什么

【Android 逆向】rpc 调用某安 App 的 X-App-Token 签名函数

作者:小陈
  • 2022 年 8 月 04 日
  • 本文字数:3335 字

    阅读完需:约 11 分钟

阅读此文档的过程中遇到任何问题,请关注公众号【移动端Android和iOS开发技术分享】或加 QQ 群【309580013

1.目标

在学习的过程中,会遇到有些算法比较麻烦,没有办法直接还原。那我们就另辟蹊径,不去分析具体的算法实现。直接使用 rpc 的方式调用算法函数,本文章以某安 App 的 X-App-Token 签名函数为例。

2.操作环境

  • mac 系统

  • frida-dexdump:导出加固后 dex 文件

  • Charles:抓取 http 接口

  • 已 Root 安卓机:脱壳

  • Python3.8:实现 rpc 功能

  • Jadx:导出 dex 文件为源码

  • Android Studio:静态分析

3.流程

寻找切入点

通过 Charles 抓包获取到关键词为 X-App-Token,这也就是我们的切入点:


静态分析

使用查壳工具发现该 apk 使用的是 360 加固,启动 App 后,使用 frida-dexdump 的frida-dexdump -FU命令导出 dex 文件:



由于 dex 文件较多,不方便查询,使用 jadx 把多个 dex 文件导出为源码:


import os
for file in os.listdir(os.curdir): if file.find(".dex") > 0: sh = 'jadx -j 1 -r -d ./ ./' + file print(sh) os.system(sh)
复制代码


将以上的 python 脚本放到 dex 同级目录,切换到 dex 目录,并执行以上脚本,执行完成后会生成 sources 文件夹,使用 Android Studio 打开该文件夹,全局搜索 X-App-Token:



找到关键函数:


private final String[] m13135() {        String str;        Locale locale = Locale.getDefault();        String valueOf = String.valueOf(Build.VERSION.SDK_INT);        String str2 = locale.getLanguage() + '-' + ((Object) locale.getCountry());        byte[] bytes = (this.f16167.m13205() + "; ; ; " + this.f16167.m13207() + "; " + ((Object) Build.MANUFACTURER) + "; " + ((Object) Build.BRAND) + "; " + ((Object) Build.MODEL) + "; " + ((Object) Build.DISPLAY) + "; " + ((Object) C4765.m13174().m12851())).getBytes(Charsets.UTF_8);        Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)");        String encodeToString = Base64.encodeToString(bytes, 0);        Intrinsics.checkNotNullExpressionValue(encodeToString, "encodeToString(device.to…eArray(), Base64.DEFAULT)");        String sb = new StringBuilder(encodeToString).reverse().toString();        Intrinsics.checkNotNullExpressionValue(sb, "StringBuilder(device).reverse().toString()");        String replace = new Regex("\\r\\n|\\r|\\n|=").replace(sb, BuildConfig.FLAVOR);        String as = AuthUtils.getAS(this.f16166, replace);        if (C4765.m13166().m13307()) {            str = "1";        } else {            str = C4765.m13166().m13300() ? "2" : "0";        }        Intrinsics.checkNotNullExpressionValue(as, "appToken");        String r1 = this.f16167.m13203();        Intrinsics.checkNotNullExpressionValue(r1, "appMetadata.channel");        return new String[]{"User-Agent", this.f16170, "X-Requested-With", "XMLHttpRequest", "X-Sdk-Int", valueOf, "X-Sdk-Locale", str2, "X-App-Id", "com.coolapk.market", "X-App-Token", as, "X-App-Version", this.f16168, "X-App-Code", String.valueOf(this.f16169), "X-Api-Version", "12", "X-App-Device", replace, "X-Dark-Mode", str, "X-App-Channel", r1, "X-App-Mode", this.f16167.m13197().toString(), "X-App-Supported", String.valueOf(this.f16167.m13199())};    }
复制代码


删除无关代码后:


private final String[] m13135() {        byte[] bytes = (this.f16167.m13205() + "; ; ; " + this.f16167.m13207() + "; " + ((Object) Build.MANUFACTURER) + "; " + ((Object) Build.BRAND) + "; " + ((Object) Build.MODEL) + "; " + ((Object) Build.DISPLAY) + "; " + ((Object) C4765.m13174().m12851())).getBytes(Charsets.UTF_8);        String encodeToString = Base64.encodeToString(bytes, 0);        String sb = new StringBuilder(encodeToString).reverse().toString();        String replace = new Regex("\\r\\n|\\r|\\n|=").replace(sb, BuildConfig.FLAVOR);        String as = AuthUtils.getAS(this.f16166, replace);    }
复制代码


由此可看出,X-App-Token 参数由 AuthUtils.getAS 方法生成,本篇文章的目的是通过 rpc 直接调用 getAS 函数,所以不会去具体分析 getAS 方法的实现。


调用 getAS 方法的入参有两个,第一个是 context,第二个参数即为我们需要拼接的参数。看源码可知,该参数由多个参数拼成,然后再 base64。具体查看每一个函数,整理后的结果如下:


this.f16167.m13205():android_id


this.f16167.m13207():wifi 的 mac 地址


((Object) Build.MANUFACTURER):硬件制造商


((Object) Build.MODEL):系统定制商


((Object) Build.DISPLAY):显示屏参数


((Object) C4765.m13174().m12851()):这个参数为空,我们就暂且不管


你也可以直接使用命令frida-trace -UF -j '*!*getBytes*':


{  onEnter(log, args, state) {    log(`String.getBytes=${this.toString()}=`)    log(`String.getBytes(${args.map(JSON.stringify).join(', ')})`);  },
onLeave(log, retval, state) { if (retval !== undefined) { log(`<= ${JSON.stringify(retval)}`); } }}
复制代码


获取到结果如下,根据结果反推参数值


e8e69e7384cb09c0; ; ; F4:F5:DB:24:A6:E1; Xiaomi; xiaomi; MI 5X; QL1515-tiffany-build-20171026203938; null

结果

至此,我们的 getAS 函数的入参已经确定,接下来就是实现 RPC


python 源码如下:


import fridafrom flask import Flask, requestimport base64result = {}

def on_message(message, data): if message['type'] == 'send': payload = message['payload'] if "###" in payload: global result array = payload.split("###") result[array[0]] = array[1] print(message['payload']) elif message['type'] == 'error': print(message['stack'])

js_code = '''rpc.exports = { // 函数名getAS sign: function(params){ Java.perform(function(){ //拿到context上下文 var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); var context = currentApplication.getApplicationContext();
// use 加载的类路径 var AuthUtils = Java.use('com.coolapk.market.util.AuthUtils'); var sign = AuthUtils.getAS(context, params); // context,params send(params+"###"+sign); } ) }};'''
process = frida.get_usb_device().attach('酷安')script = process.create_script(js_code)script.on('message', on_message)script.load()
app = Flask(__name__)

@app.route('/get_sign')def get_sign(): device_id = request.args.get('device_id', '') mac_address = request.args.get('mac_address', '').upper() manufacturer = request.args.get('manufacturer', '') model = request.args.get('model', '') display = request.args.get('display', '') params = f'{device_id}; ; ; {mac_address}; {manufacturer}; {model}; {display}; null' base_en = base64.encodebytes(params.encode('utf-8')).decode('utf-8') base_en = base_en[::-1] base_en = base_en.replace('\n', '') base_en = base_en.replace('\r', '') base_en = base_en.replace('=', '') script.exports.sign(base_en) sign = result[base_en] return sign

if __name__ == '__main__': app.run()
复制代码


运行 python 脚本后,浏览器调用 get_sign 方法即可获取到 X-App-Token


End

阅读此文档的过程中遇到任何问题,请关注公众号【移动端Android和iOS开发技术分享】或加 QQ 群【309580013


发布于: 12 小时前阅读数: 51
用户头像

小陈

关注

和昨天不一样 2019.03.12 加入

公众号:移动端Android和iOS开发技术分享

评论

发布
暂无评论
【Android逆向】rpc调用某安App的X-App-Token签名函数_逆向技术_小陈_InfoQ写作社区