Frida Hook的简单使用

本文详细介绍了如何通过Frida工具动态分析Android应用的网络请求,特别是签名的生成过程。首先配置了抓包环境,通过Charles抓包获取请求URL和参数。接着对APK进行反编译,找到签名生成的相关代码,然后利用Frida进行动态hook,获取到签名的输入参数和密钥。最后,通过Python实现了签名的复现,成功模拟请求。
摘要由CSDN通过智能技术生成

0x01 环境

python3.6、pycharm、安卓反编译工具jadx、样本apk包、charles抓包工具、电脑安装adb(安卓调试桥)

python使用到的库:frida

一部root的安卓6.0手机:手机上安装frida-server

手机使用usb线连接电脑,且打开usb调试。使用adb的时候,选择授权该电脑。

0x02 抓包分析

配置代理

保证手机和电脑处于同一网络环境下,然后手机上设置电脑IP作为其代理IP。

查询电脑IP的方法:WIN+R打开运行->输入cmd->回车打开命令提示符->输入ipconfig,显示的内容中即有电脑IP信息。如:

… …
无线局域网适配器 WLAN:

连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::1ea:ac16:a5f4:70dc%23
IPv4 地址 . . . . . . . . . . . . : 172.16.156.26
子网掩码 . . . . . . . . . . . . : 255.255.252.0
默认网关. . . . . . . . . . . . . : 172.16.156.1

则电脑IP为172.16.156.26。

手机上打开当前连接的WIFI(不同手机操作方法略有不同),修改网络->高级选项->代理->手动,代理服务器主机名填上面的172.16.156.26,端口为charles的默认端口8888

抓包

打开charles和手机,在样本apk中搜索。

拿到目标链接:

url:
https://api.xxxx.com/community/search/items/get?protocol=1&appKey=47cda90997074f10&start=0&versionCode=211&token=eed96c88-c110-4faa-bd9b-7fa6d6f0264a&key=%25E5%25A4%25A7%25E7%258E%258B&timestamp=1606190200165&count=20&channel=1002&type=3&sign=wUAXTQxOFolZP4FQuSXtZvtnn.E=

请求头:
Accept: application/json
Content-type: application/json
Host: api.xxxx.com
Connection: Keep-Alive
Cookie: acw_tc=ac11000116061900179143931e01610f6f99e840c7cae1c380c9a0a984caa7
Cookie2: $Version=1

返回内容:
{
    "rc": 0,
    "msgInfo": null,
    "errorInfo": null,
    "redirect": null,
    "redirectSign": null,
    "ccdVersion": null,
    "serverTime": null,
    "serAppInfo": null,
    "logTrackInfo": null,
    "adList": null,
    "resultUserList": [{
        "logTrackInfo": "{\"itemId\":18000119,\"itemType\":\"uid\"}",
        "algorithmId": null,
        "uid": 18000119,
        "displayName": "大王",
    ... ...
}

可以发现url中别的参数都是固定或可知的,只有sign参数未知,且不知构造方法。所以下面需要分析apk包,找到生成sign的方法。

0x03 apk反编译

打开反编译工具jadx,将样本apk包拖进去,ctrl+shift+f打开搜索,直接搜索sign=,这里可以看到有两条符合的结果:
在这里插入图片描述

而第二条左边有一个可疑的方法名叫getRequestParam,直接双击进入代码:
在这里插入图片描述

可以看到红框的代码是遍历参数,拼接字符串,再拼接一个config.getAppSecret()方法生成的字符串,最后再传入Sha1Util.calculateRFC2104HMAC中生成sign。

进入Sha1Util.calculateRFC2104HMAC可以看到,加密的过程是将传入的字符串和密钥转为bytes类型,使用HMAC_SHA1加密,然后再使用base64加密转为字符串。
在这里插入图片描述

这里知道了加密的过程,剩下就需要知道传入的参数:拼接出来的字符串str和密钥str2,分别是什么。

方法有三种:

  1. 静态分析JAVA源码
  2. 动态调试该apk
  3. 使用hook的方法拿到传入的参数样式

这里选择使用hook的方法。

0x04 hook

目前主流的hook方法有两种:xposed、frida,这里选择frida。

frida的环境安装可网上查找教程。

frida-server下载地址:https://github.com/frida/frida/releases

运行frida-server

打开cmd->运行adb shell,进入手机命令行,执行:

su
cd data/local/tmp
./frida-server

若没报错则代表执行成功。

再开一个cmd,执行端口转发:

adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043

python运行frida

代码:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-


import frida, sys

def message(message,data):
    if message["type"] == 'send':
        print("[*] {0}".format(message["payload"]))
    else:
        print(message)


def main():
    # com.xxx.xxx.util.Sha1Util是calculateRFC2104HMAC所在的类名
    jscode = """
        Java.perform(function(){
            var hook_cls = Java.use('com.xxx.xxx.util.Sha1Util')
            hook_cls.calculateRFC2104HMAC.implementation = function(a,b){
                console.log("Hook Start...");
                send(arguments);
                var ret = this.calculateRFC2104HMAC(a,b);
                send(ret);
                return ret;
            }
        }
        );
    """
    # 这里的com.xxx.xxx是apk的包名
    process = frida.get_remote_device().attach('com.xxx.xxx')
    script = process.create_script(jscode)
    script.on("message", message)
    script.load()
    sys.stdin.read()


if __name__ == '__main__':
    main()

手机打开样本apk后,运行上面代码,注意一定是先在手机打开app后再运行代码,顺序不能错了,否则可能hook失败。

查找apk包名的方法有很多,这里介绍其中一种,手机上运行该app,打开cmd执行:

adb shell dumpsys window | findstr mCurrentFocus

显示:

mCurrentFocus=Window{8d82372 u0 com.xxx.xxx/com.xxx.xxx.MainTabActivity}

其中com.xxx.xxx即为包名。

开始hook
在这里插入图片描述

pycharm中输出的结果:

Hook Start...
[*] {'0': 'demokey大王011002160620003087520211347cda90997074f10eed96c88-c110-4faa-bd9b-7fa6d6f0264ademokey', '1': 'demokey'}
[*] QJHyIV3bywcxuTugNLGz1EfWq0o=

抓包的url:

https://api.xxxx.com/community/search/items/get?protocol=1&appKey=47cda90997074f10&start=0&versionCode=211&token=eed96c88-c110-4faa-bd9b-7fa6d6f0264a&key=%25E5%25A4%25A7%25E7%258E%258B&timestamp=1606200030875&count=20&channel=1002&type=3&sign=QJHyIV3bywcxuTugNLGz1EfWq0o=

对比抓包得到的sign和hook得到的sign,可以看到hook成功,而入参str为:

demokey大王011002160620003087520211347cda90997074f10eed96c88-c110-4faa-bd9b-7fa6d6f0264ademokey

密钥str2为:

demokey

所以我们看到的sgin就是对demokey大王011002160620003087520211347cda90997074f10eed96c88-c110-4faa-bd9b-7fa6d6f0264ademokey进行HMAC_SHA1加密,然后再使用base64加密后,就得到了QJHyIV3bywcxuTugNLGz1EfWq0o=

python实现:

import base64
import hmac
from hashlib import sha1

key = 'demokey'
data = 'demokey大王011002160620003087520211347cda90997074f10eed96c88-c110-4faa-bd9b-7fa6d6f0264ademokey'

hmac_code = hmac.new(key.encode(), data.encode(), sha1).digest()
print(base64.b64encode(hmac_code, altchars=b'._').decode())

而入参str就是由url中的各个参数排序后加上密钥demokey拼接而来:

密钥 + 各参数排序的结果拼接 + 密钥

0x05 请求demo

#!/usr/bin/env python
# -*- coding: UTF-8 -*-


import base64
import hmac
import re
from hashlib import sha1
import urllib.request
from urllib.parse import urlparse, parse_qs, unquote, quote

def format_headers(data):
    param = {}
    pattern = re.compile(r'([A-Za-z\d-]*):(.*?)$', re.M)
    items = re.findall(pattern, data)
    for key, value in items:
        if key.strip() in param:
            param[key.strip()] = '{}; {}'.format(param[key.strip()], value.strip())
        else:
            param[key.strip()] = value.strip()
    return param

keyword = '纸尿裤'
start = '20'
timestamp = '1606202497355'
# 如果是第一页,type设置为3
b_type = '3' if start == '0' else '2'
keyword = quote(quote(keyword))

base_url = 'https://www.xxxx.com/community/search/items/get?protocol=1&appKey=47cda90997074f10&start={0}&versionCode=211&token=eed96c88-c110-4faa-bd9b-7fa6d6f0264a&key={1}&timestamp={2}&count=20&channel=1002&type={3}'.format(
    start, keyword, timestamp, b_type
)

app_key = 'demokey'
query = urlparse(base_url).query
params = parse_qs(query)
param_value = [v[0] for v in params.values()]
param_value.sort()
data = app_key + ''.join([unquote(item) for item in param_value]) + app_key

hmac_code = hmac.new(app_key.encode(), data.encode(), sha1).digest()
sign = base64.b64encode(hmac_code, altchars=b'._').decode()

url = base_url + '&sign={}'.format(sign)

headers = '''
Accept: application/json
Content-type: application/json
Host: api.xxxx.com
Cookie: acw_tc=ac11000116061993369595582e0161fd5568e4a923920244fa6d166c20dfb6
Cookie2: $Version=1
'''

headers = format_headers(headers)
# 发现该app,python3直接使用requests会拒绝访问,所以改用urllib
req = urllib.request.Request(url, headers=headers)
html = urllib.request.urlopen(req).read()
print(bytes.decode(html, encoding='utf-8'))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值