Simple use of Frida Hook

Posted by shah on Thu, 27 Jan 2022 20:58:33 +0100

0x01 environment

python3.6. pycharm, Android decompiler jadx, sample apk package, charles packet capture tool, computer installation ADB (Android debugging bridge)

Library used by python: frida

A root Android 6.0 mobile phone: Frida server is installed on the mobile phone

The mobile phone uses usb cable to connect to the computer, and turns on usb debugging. When using adb, choose to authorize the computer.

0x02 packet capture analysis

Configure agent

Ensure that the mobile phone and the computer are in the same network environment, and then set the computer IP on the mobile phone as its proxy IP.

How to query the computer IP: WIN+R open run - > Enter CMD - > Enter to open the command prompt - > enter ipconfig, and the computer IP information will be displayed. For example:

... ...
WLAN adapter WLAN:

Connection specific DNS suffix
Local link IPv6 address fe80::1ea:ac16:a5f4:70dc%23
IPv4 address...........: 172.16.156.26
Subnet mask...........: 255.255.252.0
Default gateway 172.16.156.1

Then the computer IP is 172.16.156.26.

Open the currently connected WIFI on the mobile phone (the operation methods of different mobile phones are slightly different), modify the network - > Advanced Options - > proxy - > manual, fill in 172.16.156.26 for the host name of the proxy server, and the port is the default port 8888 of charles.

Grab bag

Open charles and mobile phone and search in the sample apk.

Get the target link:

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=

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

Return content:
{
    "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": "king",
    ... ...
}

It can be found that other parameters in the url are fixed or known. Only the sign parameter is unknown and the construction method is unknown. Therefore, we need to analyze the apk package and find the method to generate the sign.

0x03 apk decompile

Open the decompilation tool jadx, drag the sample apk package in, ctrl+shift+f to open the search, and directly search for sign =. Here you can see two matching results:

On the left of the second item, there is a suspicious method named getRequestParam. Double click directly to enter the code:

You can see that the code in the red box is to traverse parameters, splice strings, and then splice a config The string generated by getappsecret() method, and finally passed into sha1util Calculaterfc2104hmac generates a sign.

Enter sha1util Calculaterfc2104hmac shows that the encryption process is to convert the incoming string and key into bytes and use HMAC_SHA1 encryption, and then use base64 encryption to convert to string.

Now that you know the encryption process, you need to know the incoming parameters: the spliced string str and the key str2.

There are three methods:

  1. Static analysis JAVA source code
  2. Dynamically debug the apk
  3. Use the hook method to get the passed in parameter style

Here, choose the method of using hook.

0x04 hook

At present, there are two mainstream hook methods: xposed and frida. frida is selected here.

frida environment installation can be found online tutorial.

Frida server download address: https://github.com/frida/frida/releases

Run Frida server

Open CMD - > Run adb shell, enter the command line of the mobile phone, and execute:

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

If no error is reported, the execution is successful.

Open another cmd to execute port forwarding:

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

python running frida

code:

#!/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 is the class name of 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;
            }
        }
        );
    """
    # Here's com xxx. XXX is the package name of 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()

After the mobile phone opens the sample apk, run the above code. Note that the code must be run after the mobile phone opens the app. The order cannot be wrong, otherwise the hook may fail.

There are many ways to find the apk package name. Here is one of them. Run the app on the mobile phone and open cmd to execute:

adb shell dumpsys window | findstr mCurrentFocus

Display:

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

Including com xxx. XXX is the package name.

Start hook

Output results in pycharm:

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

url for capturing packets:

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=

Comparing the sign obtained by capturing packets with the sign obtained by hook, we can see that hook is successful, and the input parameter str is:

demokey Dawang 01100216062000030875211347 cda90997074f10eed96c88-c110-4faa-bd9b-7fa6d6f0264ademokey

The key str2 is:

demokey

So the sgin we see is HMAC for demokey king 011002160620030875211347cda90997074f10eed96c88-c110-4faa-bd9b-7fa6d6f0264ademokey_ After SHA1 encryption and base64 encryption, QJHyIV3bywcxuTugNLGz1EfWq0o =.

python implementation:

import base64
import hmac
from hashlib import sha1

key = 'demokey'
data = 'demokey Dawang 01100216062000030875211347 cda90997074f10eed96c88-c110-4faa-bd9b-7fa6d6f0264ademokey'

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

The input parameter str is spliced by sorting the parameters in the url and adding the key demokey:

secret key + Splicing of sorting results of various parameters + secret key

0x05 request 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 = 'Diapers'
start = '20'
timestamp = '1606202497355'
# If it is the first page, set the type to 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)
# If the app is found, python 3 will refuse access if it directly uses requests, so it uses urllib instead
req = urllib.request.Request(url, headers=headers)
html = urllib.request.urlopen(req).read()
print(bytes.decode(html, encoding='utf-8'))