吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7922|回复: 24
收起左侧

[Android 原创] frida获得阿里系APP签名函数地址及调用

  [复制链接]
tzwsoho 发表于 2021-8-10 02:33
本帖最后由 tzwsoho 于 2021-8-10 12:01 编辑

最近研究了一下几羊的APP,写了个基于frIDA的自动抽奖脚本,这里我单独对其中的签名函数地址的获取做一下解读。
首先,包括几羊APP在内,所有阿里系APP在向服务器发出HTTPS请求时,都会带上一个签名sign,根据大佬的文章(蚂蚁森林自动收能量:https://blog.csdn.net/dieTicket/article/details/102678054)可以知道,这个签名是动态加载libsgmain.so文件,然后调用里面的
[Plain Text] 纯文本查看 复制代码
com.alibaba.wireless.security.open.securesignature.a.signRequest(Lcom/alibaba/wireless/security/open/SecurityGuardParamContext;Ljava/lang/String;)Ljava/lang/String;

得出的,其中SecurityGuardParamContext参数我们可以简单地通过jadx反编译得出是这么一个结构体:
[Java] 纯文本查看 复制代码
public class SecurityGuardParamContext {
    public String appKey;
    public Map<String, String> paramMap = new HashMap();
    public int requestType;
    public String reserved1;
    public String reserved2;
}

知道了这点,我们先来对这个函数下钩子,看看signRequest的参数分别是什么。

先准备一台root掉的手机,或者雷电模拟器,启动frida-server,这个网上很多教程,这里不再赘述。


然后将以下代码保存为hook.js:
[Asm] 纯文本查看 复制代码
function hook() {
        if (!Java.available) {
                console.error('Java API not available');
                return;
        }

        Java.perform(function () {
                console.log('hooked');

                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                Java.enumerateClassLoaders({
                        onMatch: function (loader) {
                                try {
                                        if (loader.findClass('com.alibaba.wireless.security.open.securesignature.a')) {
                                                // console.log(loader);
                                                Java.classFactory.loader = loader;

                                                Java.use('com.alibaba.wireless.security.open.securesignature.a').signRequest.implementation = function (p0, p1) {
                                                        const ret = this.signRequest(p0, p1);
                                                        console.log('p0 =', JSON.stringify(p0), 'p1 =', JSON.stringify(p1), 'sign =', ret);
                                                        return ret;
                                                };
                                        }
                                } catch (e) {
                                        // console.error('enumerateClassLoaders err', e.stack);
                                }
                        },
                        // DO NOT REMOVE 'onComplete' FUNCTION
                        onComplete: function () {
                        }
                });
        });
}

hook();


获得几羊APP的PID
[Plain Text] 纯文本查看 复制代码
# frida-ps -Uia
PID  Name    Identifier
----  ------  ---------------------------
2075  几羊      com.snail.android.lucky
1563  设置      com.android.settings
1911  雷电游戏中心  com.android.flysilkworm
...


将脚本注入几羊APP
[Plain Text] 纯文本查看 复制代码
# frida -U --no-pause -l hook.js -p 2075


然后任意点击几羊APP的界面,使几羊产生HTTPS请求,这时可以看到控制台会输入类似以下的字符串:
[Plain Text] 纯文本查看 复制代码
hooked
p0 = "<instance: com.alibaba.wireless.security.open.SecurityGuardParamContext>" p1 = "" sign = 89aa0266ec4a5e631b82209171d49548
p0 = "<instance: com.alibaba.wireless.security.open.SecurityGuardParamContext>" p1 = "" sign = 3a5774c3aa6ec736334f0c7f11bb5e53
p0 = "<instance: com.alibaba.wireless.security.open.SecurityGuardParamContext>" p1 = "" sign = 0dfca532961a1998237e2903676211f9
p0 = "<instance: com.alibaba.wireless.security.open.SecurityGuardParamContext>" p1 = "" sign = bac3f23414b3345b84c0b7654610d00f
p0 = "<instance: com.alibaba.wireless.security.open.SecurityGuardParamContext>" p1 = "" sign = c6f37ba76d12b97db25946b04039f6ed
p0 = "<instance: com.alibaba.wireless.security.open.SecurityGuardParamContext>" p1 = "" sign = 437fb3b5e9f3026d88347e6b1ff2eb34
p0 = "<instance: com.alibaba.wireless.security.open.SecurityGuardParamContext>" p1 = "" sign = 97793a1b0cdd3108fc4b01d7f2a35361


为了进一步看清p0参数的内容,我们将hook.js脚本改一下:
[JavaScript] 纯文本查看 复制代码
function stringifyMap(m) {
        var HashMapEntry = Java.use('java.util.HashMap$HashMapEntry');
        var entrySet = m.entrySet();
        var it = entrySet.iterator();
        var obj = {};
        while (it.hasNext()) {
                var node = Java.cast(it.next(), HashMapEntry);
                var key = node.getKey().toString();
                var value = node.getValue().toString();
                obj[key] = value;
        }

        return JSON.stringify(obj);
}

function hook() {
        if (!Java.available) {
                console.error('Java API not available');
                return;
        }

        Java.perform(function () {
                console.log('hooked');

                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                Java.enumerateClassLoaders({
                        onMatch: function (loader) {
                                try {
                                        if (loader.findClass('com.alibaba.wireless.security.open.securesignature.a')) {
                                                // console.log(loader);
                                                Java.classFactory.loader = loader;

                                                Java.use('com.alibaba.wireless.security.open.securesignature.a').signRequest.implementation = function (p0, p1) {
                                                        const ret = this.signRequest(p0, p1);
                                                        console.log('appKey =', p0.appKey.value, 'paramMap =', stringifyMap(p0.paramMap.value), 'requestType =', p0.requestType.value);
                                                        return ret;
                                                };
                                        }
                                } catch (e) {
                                        // console.error('enumerateClassLoaders err', e.stack);
                                }
                        },
                        // DO NOT REMOVE 'onComplete' FUNCTION
                        onComplete: function () {
                        }
                });
        });
}

hook();


这里不用重新使用frida加载hook.js,frida会自动检测到文件改变,自动重载脚本文件,这也是frida强大的功能之一。


然后我们继续点击几羊界面,可以看到类似以下的输出:
[Plain Text] 纯文本查看 复制代码
hooked
appKey = SNAIL_APP_KEY_ANDROID paramMap = {"INPUT":"Operation-Type=alipay.mobile.aggrbillinfo.user.sign&Request-Data=W3siYXBkaWQiOiJlWU9Ja2tud0tMZndBVFNaQ2VUL3RkdDdpZEQwbERYeEhOM21LWG1Od0ZCcUI2UmlHdnZ4K1B3eSIsImNsaWVudEtleSI6IlVMYmVxMUFQTnciLCJjbGllbnRWZXJzaW9uIjoiMy40LjAuOTciLCJtb2RlbCI6IlBDUlQwMCIsInBsYXRmb3JtIjoiQW5kcm9pZCIsInN5c3RlbVN3aXRjaFN0YXR1cyI6dHJ1ZSwidG9rZW4iOiI3MmQ1YWQ4ZmIwZmIwOWNiMTRjMGI1OWNkNjY2ZTJhNCIsInVzZXJJZCI6IjgwODgwMjcxMDExMDM3MDYiLCJ1dGRpZCI6IllRN2MvYUdnSjlNREFMR1dKMEEzdFhRKyJ9XQ==&Ts=Nii7Fou"} requestType = 4
appKey = SNAIL_APP_KEY_ANDROID paramMap = {"INPUT":"Operation-Type=alipay.mobile.aggrbillinfo.message.new.index&Request-Data=W3siYXBkaWQiOiJlWU9Ja2tud0tMZndBVFNaQ2VUL3RkdDdpZEQwbERYeEhOM21LWG1Od0ZCcUI2UmlHdnZ4K1B3eSIsImNsaWVudEtleSI6IlVMYmVxMUFQTnciLCJjbGllbnRWZXJzaW9uIjoiMy40LjAuOTciLCJtb2RlbCI6IlBDUlQwMCIsInBsYXRmb3JtIjoiQW5kcm9pZCIsInRva2VuIjoiNzJkNWFkOGZiMGZiMDljYjE0YzBiNTljZDY2NmUyYTQiLCJ1c2VySWQiOiI4MDg4MDI3MTAxMTAzNzA2IiwidXRkaWQiOiJZUTdjL2FHZ0o5TURBTEdXSjBBM3RYUSsifV0=&Ts=Nii7Gay"} requestType = 4


我们可以看到appKey的值固定是字符串SNAIL_APP_KEY_ANDROID

paramMap里面固定是INPUT做key,后面的Value是一串由Operation-TypeRequest-DataTs三个参数组成的请求字符串,其中:
Operation-Type是操作类型名,表示要求服务器做的操作,
Request-Data其实是一串Base64字符串,我们随意解码一段:
[JavaScript] 纯文本查看 复制代码
[{"apdid":"eYOIkknwKLfwATSZCeT/tdt7idD0lDXxHN3mKXmNwFBqB6RiGvvx+Pwy","clientKey":"ULbeq1APNw","clientVersion":"3.4.0.97","model":"PCRT00","platform":"Android","systemSwitchStatus":true,"token":"72d5ad8fb0fb09cb14c0b59cd666e2a4","userId":"8088027101103706","utdid":"YQ7c/aGgJ9MDALGWJ0A3tXQ+"}]

可以看到里面包含了这些内容,那我们可以按这些字段去jadx搜索,就可以知道这些值是怎么得来的。

Ts其实就是取当时时间毫秒数,用自定义的64进制转换方法转换成的字符串,在开头提到的文章《蚂蚁森林自动收能量》里面有说明,这里不再赘述。

最后的requestType,这个值我们通过jadx反编译(com.alipay.mobile.common.netsdkextdepend.security.DefaultSecurityManager.signature(SignRequest signRequest))得知,使用MD5算法时是4。
还有reserved1和reserved2是保留用的,都是空字符串,这里不再列出。

知道了上面这些参数的含义,我们可以写个函数来模拟调用signRequest函数并导出,从而自己计算sign签名的值,最终可以模拟各种各样阿里系APP的HTTPS请求!

[JavaScript] 纯文本查看 复制代码
function signRequest(p) {
        // console.log('operationType =', p.operationType);
        // console.log('requestData =', p.requestData);
        // console.log('ts =', p.ts);

        if (!securesignature || !securesignature.signRequest) {
                console.error('securesignature instance not found, please click homepage first!');
                return '';
        }

        const clsSecurityGuardParamContext = Java.use('com.alibaba.wireless.security.open.SecurityGuardParamContext');
        const instSecurityGuardParamContext = clsSecurityGuardParamContext.$new();

        const appKeyField = clsSecurityGuardParamContext.class.getDeclaredField('appKey');
        const requestTypeField = clsSecurityGuardParamContext.class.getDeclaredField('requestType');
        const paramMapField = clsSecurityGuardParamContext.class.getDeclaredField('paramMap');

        // Set fields accessable
        appKeyField.setAccessible(true);
        requestTypeField.setAccessible(true);
        paramMapField.setAccessible(true);

        // Make map
        const HashMap = Java.use("java.util.HashMap");
        const paramHashMap = HashMap.$new();

        const INPUT = `Operation-Type=${p.operationType}&Request-Data=${p.requestData}&Ts=${p.ts}`;
        paramHashMap.put('INPUT', INPUT);

        // Set values
        appKeyField.set(instSecurityGuardParamContext, 'SNAIL_APP_KEY_ANDROID');
        requestTypeField.setInt(instSecurityGuardParamContext, 4);
        paramMapField.set(instSecurityGuardParamContext, paramHashMap);

        // Print values and verify
        // var appKey = appKeyField.get(instSecurityGuardParamContext);
        // var requestType = requestTypeField.get(instSecurityGuardParamContext);
        // var paramMap = paramMapField.get(instSecurityGuardParamContext);
        // console.log(
                // 'appKey =', appKey, '\n',
                // 'requestType =', requestType, '\n',
                // 'paramMap =', stringifyMap(Java.cast(paramMap, HashMap)),
                // '\n', '*'.repeat(100));

        return securesignature.signRequest(instSecurityGuardParamContext, '');
}

rpc.exports = {
        signRequest,
};


完整的代码在这里:https://github.com/tzwsoho/auto_snail_lucky/tree/master/sign_tool

感谢阅读,请对我的自动抽奖脚本多多支持,多多点Star,谢谢!

免费评分

参与人数 5威望 +1 吾爱币 +30 热心值 +5 收起 理由
qtfreet00 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
笙若 + 1 + 1 谢谢@Thanks!
piopiopio123 + 1 + 1 我很赞同!
timeni + 1 + 1 我很赞同!
涛之雨 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ColoThor 发表于 2021-8-10 11:41
大佬,能给个文中帖子 蚂蚁森林自动收能量 的地址吗
 楼主| tzwsoho 发表于 2021-8-11 11:03
bhwxha 发表于 2021-8-11 10:31
原始帖子发布时间是2021-8-10 02:33,然后帖子被吞了,现在放出来了,显示于2021-8-10 12:01编辑;
帖子修 ...

没吞,只是我发错版区了,原贴的csdn超链接没有显示出来,我加上了而已
QingRemix 发表于 2021-8-10 10:37
mosou 发表于 2021-8-10 10:49
有没有淘系xsign的
头像被屏蔽
asdswd 发表于 2021-8-10 11:15
提示: 作者被禁止或删除 内容自动屏蔽
 楼主| tzwsoho 发表于 2021-8-10 12:03
ColoThor 发表于 2021-8-10 11:41
大佬,能给个文中帖子 蚂蚁森林自动收能量 的地址吗

已经改过了,这论坛不知道为什么把超链接给吃了
 楼主| tzwsoho 发表于 2021-8-10 12:04
mosou 发表于 2021-8-10 10:49
有没有淘系xsign的

应该所有阿里出的APP都是用同一种签名算法的,淘宝、淘特之类的还没研究过
yangningnb 发表于 2021-8-10 17:03
阿里web系列的有吗?
ColoThor 发表于 2021-8-10 18:14
tzwsoho 发表于 2021-8-10 12:03
已经改过了,这论坛不知道为什么把超链接给吃了

好的,谢谢
咔c君 发表于 2021-8-10 21:10
学习了不错
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 13:34

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表