loushimin 发表于 2023-8-18 15:16

某药品监管app——安卓端脱壳+RPC服务调用参数生成方法

本帖最后由 loushimin 于 2023-8-22 14:13 编辑

前言:
本篇帖子是在我爬虫笔记里的的记录
只是操作记录,代码量少
主要记录了脱壳、调试、objection hook、frida hook注入,最后调用注入的方法获取加密参数


一、环境准备:
1、某药品监管apk:v5.3.2


2、安卓端脱壳工具——BlackDex:无版本要求,最新的即可
github : https://github.com/CodingGay/BlackDex/releases
脱壳环境要求

[*]一台普通手机
[*]无需Xposed
[*]无需Frida
[*]无需Magisk
[*]无需Root
[*]无需定制系统


   
3、Frida
frida                   15.2.2
frida-tools             11.0.0


4、objection
objection github:https://github.com/sensepost/objection
pip install objection
pip安装最新的即可,无版本要求


5、jadx



6、安卓真机或模拟器
安卓 7
逍遥模拟器



7、Android Studio

8、python 3.7



二、开始
1、抓包分析
通过对app的抓包可知app端就一个加密参数 tzRgz52a签名
headers:
Accept-Language: zh-CN,zh;q=0.8
User-Agent: Mozilla/5.0 (Linux; U; Android 7.1.2; zh-cn; HD1910 Build/N2G48H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
Connection: close
Host: mobile.nmpa.gov.cn
Accept-Encoding: gzip
tzRgz52a: BlRnN9IbxigK7nqPZitab6KNJ5oaPyiKKmPOSV-k_6JsT7bS0ywp0DhUwx7pDVsvshj5S5Mj26SqTg6YK_jc81sahiH4oH86rAotrTjoK8I3NYzEGP_wDWlJtGolNxD8XgDZr0z3hoyGzTI7dCuL2RI5e_uNwyIuD-yUEWz9TrTEKVO9Po1iWTPGbgfns9xN7bgjDYfbmHxiXi0IRWmBNZY2uNZ6I7b2iI2GeR2lLN_Zj42B2UP_oClQv5jdPxENikZ8GOa8UoJM4Slja0hY2k-FT6RWtjDoL6IvXIYy40WdnM0pYvMWxE2kkczs_WuN7nuFPKjZplGDwJYABG5R4qSchFSoIt_95rLa3N9OtgRvWMur9qQM3rHVZa16WqRKcYJqHwIrxiOncUS-udOLYfFtQlCLsxe0RlFpVxn0ykiw1O0ualP2_2r-mY7VoOKS0wDCM6aaWynDAfVdNQJonnRv1SjS9MjgJ0BgAMGdPwENNTUn_cAkwFK-HVZpw35H7PGqAk0DoMhNfQ9vR0XQ7p3eUkyc1Lp3bdDXL94AHZb3lVSEUicex9BumrwnkNGM3F3s4B1zjdetUk3tZZlj8u3v_Fj48wDC7zsR8-KOBkrGMBj-t2rOahfGk1PXqK8_epWVIysgbLlXInHP35587M0
Connection: close
Cache-Control: no-cache


2、frida-server
下载与frida版本对应的 frida-server,模拟器就下载 x86版本的(红框),安卓真机就下载arm版本的(蓝框),也可以adb shell 进入手机终端,执行以下命令查看内核版本,再下载相应的frida-server
adb shell
getprop ro.product.cpu.abi






将下载的frida-server-15.2.2-android-x86_64.xz解压,将文件夹中的 frida-server-15.2.2-android-x86_64 push到安卓目录 /data/local/tmp/ 下,并给该文件添加权限,然后并启动
adb shell
cd /data/local/tmp
su
#添加权限
chmod 777 frida-server-15.2.2-android-x86_64
#启动frida-server
./frida-server-15.2.2-android-x86_64


新建cmd窗口开启端口转发
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043


在cmd窗口中输入 frida-ps -U命令后输出模拟器进程,说明frida安装成功
frida-ps -U




3、app脱壳
在手机上安装 药监局app 和 BlackDex app
对于BlackDex 64位的和32位的都要安装,如果一个脱壳失败,就换另一个版本



根据提示将脱壳后的dex文件提取出来







4、调试
使用jadx打开dex文件





先在jadx中全局搜索tzRgz52a 结果是搜不到的
如果经验多的话 这时候就应该知道,先搜索与header有关的
搜索 getheader 结果如下




可以发现,搜索到了很多,重点观察com开头的节点,发现两个可以的方法com.msec.MSecClient 的 _ts_getHeaderKey, _ts_getRequestHeader





下一步就可以根据上面的结果进一步调试hook看能否拿到我们想要的数据

调试方法有很多
1、objection hook
2、python 写脚本 hook
3、fridamanager 注入 js hook
4、xposed 插件注入 js hook
5、aosp 内置 frida hook
都可进行尝试,这里用的是objection



三、objection hook
1、查找app及其包名


cmd窗口执行
frida-ps -Uai


结果如下


com.hxzk.android.hxzksyjg_xj即是包名


2、objection hook
cmd执行 ,进入objection
objection -g com.hxzk.android.hxzksyjg_xj explore




根据二、4的结果搜索msec相关的类
android hooking search classes msec




hook com.msec.MSecClient 类,查看类中的方法,找到要hook的方法
android hooking watch class com.msec.MSecClient




hook以上两个方法


android hooking watch class_method com.msec.MSecClient._ts_getH
eaderKey --dump-args --dump-backtrace --dump-return
android hooking watch class_method com.msec.MSecClient._ts_getRequestHeader --dump-args --dump-backtrace --dump-return


去app中请求接口,即可看到hook结果



根据hook结果可知 _ts_getRequestHeader()方法的返回值 即是 tzRgz52a签名

3、使用frida hook并注入,hookjs如下:
function get_yjj_tzRgz52a() {
    console.log('script 加载成功')
    Java.perform(function () {
      console.log('hook test')
      var MainActivity = Java.use('com.msec.MSecClient');
      MainActivity._ts_getRequestHeader.implementation = function (x, y, z) {
            // 这里开始是原函数的逻辑
            console.log(x)
            console.log(y)
            console.log(z)

            // 这里对原函数的参数重新赋值
            var vaule = this._ts_getRequestHeader(x, y, z);

            // 返回原函数的结果
            console.log(vaule);
            return vaule

      }
    })
}

setTimeout(get_yjj_tzRgz52a)


使用 以下命令注入:
frida -U -l 1.js 中国药品监管

注意:这里注入后面写的是name 而并非包名



如图所示,注入成功



在app中重新请求 接口,hook结果如下



0 0 http://mobile.nmpa.gov.cn/datasearch/QueryList?tableId=25&searchF=Quick%20SearchK&pageIndex=4&pageSize=15
分别对应方法的参数 x y z,多次请求后发现 0 0是固定的,即只有参数z是变量,z就是 请求的数据接口链接



4、RPC主动调用
确定方法的参数后即可使用RPC将被动调用 改为主动调用
稍微改动下hook的js
function getYjjTzRgz52a(x, y, z) {
    var vaule;
    Java.perform(function () {
      Java.choose('com.msec.MSecClient', {
            onMatch: function (instance) {
                vaule = instance._ts_getRequestHeader(x, y, z);
            },
            onComplete: function () {
                console.log('_ts_getRequestHeader()调用完成')
            }
      })
    })
    return vaule
}

rpc.exports = {
    getyjjtzrgz52a: getYjjTzRgz52a
}


RPC使用python实现,代码如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 LouShimin, Inc. All Rights Reserved
#
# @version : 1.0
# @AuThor: LouShimin
# @Time    :
# @FileName: yjj.py
# @desc    :
import frida

def on_message(message, data):
    if message['type'] == 'send':
      print(message['payload'])
    elif message['type'] == 'error':
      print(message['stack'])


device = frida.get_usb_device()
process = device.attach('中国药品监管')
with open('./yjj_end_rpc.js', encoding='utf-8') as f:
    jscode = f.read()
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
def get_yjj_rpc():
    rpc = script.exports
    return rpc

def get_yjj_tzRgz52a(page):
    rpc = get_yjj_rpc()
    a1 = rpc.getyjjtzrgz52a(0, 0,
                           'http://mobile.nmpa.gov.cn/datasearch/QueryList?tableId=25&searchF=Quick%20SearchK&pageIndex={}&pageSize=15'.format(
                                 page))
    print(a1)

if __name__ == '__main__':
    while True:
      page = input('请输入页码:')
      print(page)
      get_yjj_tzRgz52a(page)



运行程序后输入页码,返回值即为tzRgz52a 签名



5、Sanic+RPC远程调用
接口文件如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 LouShimin, Inc. All Rights Reserved
#
# @Version : 1.0
# @Author: LouShimin
# @Time    :
# @FileName: sanic_app.py
# @Desc    :
from sanic import Sanic
from sanic.response import json as response_json

from commons.frida_rpc.yjj_rpc import get_yjj_rpc

app = Sanic(__name__)

yjj_rpc = get_yjj_rpc()


@app.route("/getSignFromJni", methods=['POST'])
async def req_proxy_data(request):
    res = request.json
    yjj_url = res['yjj_url']

    tzRgz52a = yjj_rpc.getyjjtzrgz52a(0, 0, yjj_url)
    print(tzRgz52a)
    res_data = {
      'msg': 'success',
      'code': 200,
      'tzRgz52a': tzRgz52a,
      'yjj_url': yjj_url
    }
    return response_json(res_data)


if __name__ == '__main__':
    app.run("0.0.0.0", port=16383, debug=True, workers=30)



改造并引入三、4中的 getyjjtzrgz52a()方法接口通过接口形式调用:



loushimin 发表于 2023-11-21 14:47

嘛哩嘛哩轰 发表于 2023-11-12 20:38
怎么现在 5.3.2的apk 打开直接弹 禁止root 设备安装apk了。。。。

在面具中对该应用隐藏面具,再使用JsHook app对该应用hook,过掉root检测。如图:

loushimin 发表于 2023-11-21 15:09

天空宫阙 发表于 2023-11-11 11:58
rpc调用确实很方便,就是感觉不能大量调用,加密参数可能包含了设备的信息

这个我个人觉得不是太大的问题,web端可以用指纹浏览器,安卓端可以用“应用伪装”等app去修改设备的信息,所以我认为设备信息对于rpc来说只是个小坎,稍微做点改动就过去了

zhouzheng1201 发表于 2023-11-17 11:35

不错不错,支持一下

嘛哩嘛哩轰 发表于 2023-11-3 16:28

不错不错,支持一下

wantwill 发表于 2023-11-3 16:47

不错不错,支持一下

中原一点红 发表于 2023-11-4 22:26

过来看看
{:17_1077:}

mcby 发表于 2023-11-7 09:31

再学习一下大脑的技术。

Mist520 发表于 2023-11-7 11:25

感谢分享 谢谢大佬

天空宫阙 发表于 2023-11-11 11:58

rpc调用确实很方便,就是感觉不能大量调用,加密参数可能包含了设备的信息

嘛哩嘛哩轰 发表于 2023-11-12 20:38

怎么现在 5.3.2的apk 打开直接弹 禁止root 设备安装apk了。。。。

closeai 发表于 2023-11-13 11:56


不错不错,支持一下
页: [1] 2 3
查看完整版本: 某药品监管app——安卓端脱壳+RPC服务调用参数生成方法