阅读此文档的过程中遇到任何问题,请关注公众号【移动端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 frida
from flask import Flask, request
import base64
result = {}


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

 

【Android逆向】rpc调用某安App的X-App-Token签名函数的更多相关文章

  1. Ratel:一直站在Android逆向巅峰的平头哥

    本文来源:带动行业内卷,渣总义不容辞 字越少事儿越大,请关注github(可以点击阅读原文): https://github.com/virjarRatel 平头哥(ratel)是一个Android逆 ...

  2. Android逆向分析(2) APK的打包与安装背后的故事

    前言 上一次我们反编译了手Q,并遇到了Apktool反编译直接crash的问题,虽然笔者很想在这次解决这个问题,但在解决途中,发现该保护依赖于很多知识,所以本次先插入一下,正所谓知其然知其所以然,授之 ...

  3. Android逆向分析(2) APK的打包与安装

    http://blog.zhaiyifan.cn/2016/02/13/android-reverse-2/ 2/18日增加对aidl和java编译的描述. 前言 上一次我们反编译了手Q,并遇到了Ap ...

  4. Android逆向之so的半自动化逆向

    因为工作需要,转型干android逆向,有几个月了.不过对于so的逆向,任然停留在,难难难的阶段,虽然上次自己还是逆向了一个15k左右的小so文件,但是,那个基本是靠,一步一步跟代码,查看堆栈信息来自 ...

  5. Android逆向之静态分析

    想必打过CTF的小伙伴多多少少都触过Android逆向,所以斗哥将给大家整一期关于Android逆向的静态分析与动态分析.本期先带来Android逆向的静态分析,包括逆向工具使用.文件说明.例题解析等 ...

  6. Android 逆向实战篇(加密数据包破解)

    1. 实战背景由于工作需要,要爬取某款App的数据,App的具体名称此处不便透露,避免他们发现并修改加密逻辑我就得重新破解了. 爬取这款App时发现,抓包抓到的数据是加密过的,如图1所示(原数据较长, ...

  7. 【转】Android逆向入门流程

    原文:https://www.jianshu.com/p/71fb7ccc05ff 0.写在前面 本文是笔者自学笔记,以破解某目标apk的方式进行学习,中间辅以原理性知识,方便面试需求. 参考文章的原 ...

  8. 在Android Studio中调用so中的方法

    本节用的so是上节用Android Studio创建的so.想在Android Studio中调用so中的方法,需要先引用so.Android Studio中引用so的方法有二种,下面开始介绍. 一 ...

  9. Android逆向之旅---Native层的Hook神器Cydia Substrate使用详解

    一.前言 在之前已经介绍过了Android中一款hook神器Xposed,那个框架使用非常简单,方法也就那几个,其实最主要的是我们如何找到一个想要hook的应用的那个突破点.需要逆向分析app即可.不 ...

  10. Android逆向系列文章— Android基础逆向(6)

    本文作者:HAI_ 0×00 前言 不知所以然,请看 Android逆向-Android基础逆向(1) Android逆向-Android基础逆向(2) Android逆向-Android基础逆向(2 ...

随机推荐

  1. Hadoop集群运行

    在Hadoop文件参数配置完成之后 在master上操作 su - hadoop cd /usr/local/src/hadoop/ ./bin/hdfs namenode -format hadoo ...

  2. 走进Redis-扯扯集群

    集群 为什么需要切片集群 已经有了管理主从集群的哨兵,为什么还需要推出切片集群呢?我认为有两个比较重要的原因: 当 Redis 上的数据一直累积的话,Redis 占用的内存会越来越大,如果开启了持久化 ...

  3. C# 中的那些锁,在内核态都是怎么保证同步的?

    一:背景 1. 讲故事 其实这个问题是前段时间有位朋友咨询我的,由于问题说的比较泛,不便作答,但想想梳理一下还是能回答一些的,这篇就来聊一聊下面这几个锁. Interlocked AutoResetE ...

  4. Java根据Freemarker模板生成Word文件

    1.  准备模板 模板 + 数据 = 模型 1.将准备好的Word模板文件另存为.xml文件(PS:建议使用WPS来创建Word文件,不建议用Office) 2.将.xml文件重命名为.ftl文件 3 ...

  5. 使用 Loki 收集 Traefik 日志

    转载自:https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247492264&idx=1&sn=f443c92664 ...

  6. 第2篇----Istio架构概述篇

    Istio的工作机制 Istio的工作机制和架构,分为控制面和数据面两部分.控制面主要包括Pilot.Mixer.Citadel等服务组件:数据面由伴随每个应用程序部署的代理程序Envoy组成,执行针 ...

  7. [题解] Atcoder AGC 005 F Many Easy Problems NTT,组合数学

    题目 观察当k固定时答案是什么.先假设每个节点对答案的贡献都是\(\binom{n}{k}\),然后再减掉某个点没有贡献的选点方案数.对于一个节点i,它没有贡献的方案数显然就是所有k个节点都选在i连出 ...

  8. 关于WinSCP如何通过Tunnel隧道进行远程连接-进行文件的传输

    对于公司业务相关的重要的Linux服务器,一般都会限制源IP登录,一般就会使用到jumper server(跳板机) 这样一来,我们的Client就无法直接ssh/scp/sftp到目标服务器了,这样 ...

  9. P3261 [JLOI2015]城池攻占 (左偏树+标记下传)

    左偏树还是满足堆的性质,节点距离就是离最近的外节点(无左或者右儿子  或者二者都没有)的距离,左偏性质就是一个节点左儿子的距离不小于右儿子,由此得:节点距离等于右儿子的距离+1. 本题就是对于每个节点 ...

  10. 2022-08-21-xdm说个事啊

    layout: post cid: 15 title: xdm说个事啊 slug: 15 date: 2022/08/21 13:06:34 updated: 2022/08/21 13:06:34 ...