美图app sig参数分析

本文案例是对美图秀秀app的sig参数和client_session参数生成分析。

案例环境:美图版本9.3.5 、工具Jadx、夜神安卓7、Frida

APP包名: com.mt.mtxx.mtxx

@toc


接口分析

在这里插入图片描述

抓包,搜索接口和参数如下:

url = "https://api.xiuxiu.meitu.com/v2/search/feeds.json"
params = {
    "client_timestamp": "1639113315051",
    "client_timezone": "GMT+8",
    "is_gdpr": "0",
    "client_channel_id": "baidu",
    "client_model": "MI 9",
    "cpuModel": "shamu",
    "client_brand": "Android",
    "resolution": "900*1600",
    "client_id": "1089867602",
    "sigVersion": "1.3",
    "runtimeMaxMemory": "192",
    "ad_sdk_version": "5.7.20",
    "feed_sort": "normal",
    "is_test": "0",
    "keyword": "韩国证件照",
    "client_network": "wifi",
    "ram": "3482",
    "cpuVendor": "Other",
    "count": "12",
    "version": "9.3.5.0",
    "search_type": "0",
    "is_basic": "0",
    "country_code": "CN",
    "app_hot_start_times": "1",
    "attachFlag": "409",
    "client_session": "8b5f0ef740d6d22fd3a5c5e7cd069e45",
    "is64Bit": "0",
    "client_is_root": "2",
    "client_operator": "CHINA MOBILE",
    "gid": "2638425610",
    "mac": "08:00:27:97:F2:76",
    "client_language": "zh_CN",
    "cn_switch_on": "0",
    "sig": "50a300ed42c1202deb5439a77c3dcc70",
    "sigTime": "1639113315054",
    "client_os": "5.1.1",
    "lang": "1",
    "is_privacy": "0",
    "user_agent": "mtxx-9350-Xiaomi-MI 9-android-5.1.1-9d212252",
    "is_ohos": "0",
    "sigEnv": "0",
    "appAreaType": "3",
    "personality_not_recommend": "0",
    "community_version": "2.0.0",
    "android_sdk_int": "22",
    "ad_personality_not_recommend": "0",
    "imei": "863254944222826",
    "is_device_support_64": "0",
    "android_id": "e3be29a75f28a5c2"
}

经过分析, 动态参数有 client_timestamp、client_session、sig、sigTime。

client_timestamp,sigTime是时间戳,client_session、sig 应该是加密参数。

接口分析完成,接下来进行源码分析。


源码分析

查壳,尬住,查出来是阿里聚安全。
在这里插入图片描述

这个壳没啥好脱法,所以不脱了,直接丢jadx中反编译。

在这里插入图片描述

8G内存吃满了,没办法我修改成了12G,如果你没这么多内存就换其他工具,Apktools或者JEB反编译。

内存加载完成之后,占了8.2G。

开始静态搜索加密参数,和sig相关的搜索词有很多,"sig"、sig;、"sig、=sig 等等。
在这里插入图片描述
经过多次查看,最后找到了比较符合的位置。
在这里插入图片描述
进去之后,观察文件内容,有很多SigEntity重载方法。
在这里插入图片描述
那如何知道生成sig的是哪一个方法呢,这里最好是hook查看。


Frida hook

启动frida
在这里插入图片描述

不知道参数名该怎么写的话,就随便写两个,然后看frida的报错提示就可以。

{
'type': 'error', 'description': "Error: generatorSig(): specified argument types do not match any of:
overload('java.lang.String', '[Ljava.lang.String;', 'java.lang.String')
overload('java.lang.String', '[Ljava.lang.String;', 'java.lang.String', 'java.lang.Object')",
}

这是报错中给出的参数类型,直接拿来用。每个都测一下就知道哪个是生成sig的方法了。

Frida HOOK代码如下:

import frida, sys

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

jscode_hook = """
Java.perform(
    function(){
            console.log("1. start hook");
            var ba = Java.use("com.meitu.secret.SigEntity");
            if (ba != undefined) {
                console.log("2. find class");
                ba.generatorSig.overload('java.lang.String', '[Ljava.lang.String;', 'java.lang.String', 'java.lang.Object').implementation = function (a1,a2,a3,a4) {
                    console.log("3. find function");
                    console.log(a1);
                    console.log(a2);
                    console.log(a3);
                    console.log(a4);
                    var res = ba.generatorSig(a1,a2,a3,a4);
                    console.log("计算result:" + res);
                    return res;
                }
            }
    }
)
"""

process = frida.get_usb_device().attach('com.mt.mtxx.mtxx')
script = process.create_script(jscode_hook)
script.on('message', on_message)
print('[*] Hook Start Running')
script.load()
sys.stdin.read()

触发请求,查看frida打印内容。
在这里插入图片描述

  • 第一个参数是:search/feeds.json
  • 第二个参数是:提交的参数、包括设备环境
  • 第三个参数是:6184556633574670337
  • 第四个参数是:com.meitu.remote.hotfix.app.RemoteHotfixApplication@3fbe8c4

经过多次测试,除了提交的参数,比如搜索词之外,其他都是定值。 需要注意的是第四个参数是Object对象。

在这里插入图片描述
根据代码逻辑判断,只有 obj符合Context类型时,才会生成sig,所以可以笃定obj是一个Context对象。

攒够了参数之后,就可以根据方法去编写加密算法了,但是查看了一下加密函数的逻辑,调用了native中的方法。
在这里插入图片描述
秉承着不分析so文件的思路,此时有两种解决方法。通过unidbg去模拟调用so中的方法;或者依旧通过frida来RPC导出函数生成sig。


sig生成

import frida, sys

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

jscode_hook_2 = """
    Java.perform(
    function(){
        console.log('Hook Start !');
        var SigEntity = Java.use('com.meitu.secret.SigEntity');
        var BaseApplication = Java.use('com.meitu.library.application.BaseApplication');
        var str1 = "search/feeds.json";
        var strArr =  ["1639127762948","CMCC","GMT+8","0","2638419901","baidu","LIO-AN00","kirin990","HUAWEI","900*1600","zh_CN","08:00:27:CF:AE:71","1089867602","0","192","5.7.20","7.1.2","hot","0","闪闪","1","0","wifi","mtxx-9350-HUAWEI-LIO-AN00-android-7.1.2-879a8b83","3038","华为","12","0","0","1","0","9.3.5.0","0","0","2.0.0","CN","1","409","25","0","8c4f7fae2f81d530ecc10f372b9ce0f1","0","863064425644392","0","8fbf78cea9289fd1","2"]
        var str3 = "6184556633574670337";
        var content = SigEntity.getContext();
        var signResult = SigEntity.generatorSig(str1, strArr, str3, content);
        console.log("sig: ", signResult.sig.value)
        console.log("sigTime: ", signResult.sigTime.value)
        console.log("sigVersion: ", signResult.sigVersion.value)
    }
)
"""

process = frida.get_usb_device().attach('com.mt.mtxx.mtxx')
script = process.create_script(jscode_hook_2)
script.on('message', on_message)
print('[*] Hook Start Running')
script.load()
sys.stdin.read()

执行结果:
在这里插入图片描述


Frida Flask PRC

正常的RPC导出代码:

import frida

def on_message(message, data):
    print("[%s] => %s" % (message, data))

def start_hook():
    session = frida.get_usb_device().attach('com.mt.mtxx.mtxx')
    print("[*] start hook")

    js_code = '''
    rpc.exports = {
        "a": function (kw) {
            var ret = {};
            Java.perform(function(){
                    var SigEntity = Java.use("com.meitu.secret.SigEntity");
                    var BaseApplication = Java.use('com.meitu.library.application.BaseApplication');
                        var str1 = "search/feeds.json";
                        var str3 = "6184556633574670337";
                        var content = BaseApplication.getApplication();
                    var result = SigEntity.generatorSig(str1, kw, str3, content);
                    ret["result"]=result.sig.value;
                    console.log("[*] Sig:",result.sig.value);
                    }
                )
            return ret;
            }
        }
    '''
    script = session.create_script(js_code)
    script.on('message', on_message)
    script.load()
    return script

kw = '闪闪'
params = ['1639127762948', 'CMCC', 'GMT+8', '0', '2638419901', 'baidu', 'LIO-AN00', 'kirin990', 'HUAWEI', '900*1600','zh_CN', '08:00:27:CF:AE:71', '1089867602', '0', '192', '5.7.20', '7.1.2', 'hot', '0', kw, '1', '0', 'wifi','mtxx-9350-HUAWEI-LIO-AN00-android-7.1.2-879a8b83', '3038', '华为', '12', '0', '0', '1', '0', '9.3.5.0', '0','0', '2.0.0', 'CN', '1', '409', '25', '0', '8c4f7fae2f81d530ecc10f372b9ce0f1', '0', '863064425644392', '0','8fbf78cea9289fd1', '2']
result = start_hook().exports.a(params)
print(result)

通过Flask配合Frida实现RPC远程调用。

import frida
from flask import Flask, jsonify, request

app = Flask(__name__)

def on_message(message, data):
    print("[%s] => %s" % (message, data))

def start_hook():
    session = frida.get_usb_device().attach('com.mt.mtxx.mtxx')
    js_code = '''
    rpc.exports = {
        "a": function (kw) {
            var ret = {};
            Java.perform(function(){
                    var SigEntity = Java.use("com.meitu.secret.SigEntity");
                    var BaseApplication = Java.use('com.meitu.library.application.BaseApplication');
                        var str1 = "search/feeds.json";
                        var str3 = "6184556633574670337";
                        var content = BaseApplication.getApplication();
                    var result = SigEntity.generatorSig(str1, kw, str3, content);
                    ret["result"]=result.sig.value;
                    console.log("[*] Sig:",result.sig.value);
                    }
                )
            return ret;
            }
        }
    '''
    script = session.create_script(js_code)
    script.on('message', on_message)
    script.load()
    return script

@app.route("/hook")
def search():
    kw = request.args.get("kw")
    params = ['1639127762948', 'CMCC', 'GMT+8', '0', '2638419901', 'baidu', 'LIO-AN00', 'kirin990', 'HUAWEI', '900*1600','zh_CN', '08:00:27:CF:AE:71', '1089867602', '0', '192', '5.7.20', '7.1.2', 'hot', '0', kw, '1', '0', 'wifi','mtxx-9350-HUAWEI-LIO-AN00-android-7.1.2-879a8b83', '3038', '华为', '12', '0', '0', '1', '0', '9.3.5.0', '0','0', '2.0.0', 'CN', '1', '409', '25', '0', '8c4f7fae2f81d530ecc10f372b9ce0f1', '0', '863064425644392', '0','8fbf78cea9289fd1', '2']
    result = start_hook().exports.a(params)
    return jsonify({'result':result})

if __name__ == '__main__':
    app.run()

示例:
在这里插入图片描述


client_session分析

接着再看一下 client_session
在这里插入图片描述
点进去
在这里插入图片描述
m77880a方法
在这里插入图片描述

m74573a 和 m74574a ,这是一个md5算法。
在这里插入图片描述
所以 client_session的生成很简单。

点赞

发表回复