某鱼App加密参数x-sign生成教程

通过对某鱼App抓包,可发现数据请求接口中都必须携带x-sign参数,而x-sign参数的生成方式不得而知,只能通过对App进行反编译破解。本教程将利用HermesAgent框架实现对x-sign生成方法的Hook及远程调用签名方法。

本教程以某鱼App v6.5.10版本为例

第一步:反编译某鱼App

利用dex2jar工具反编译某鱼APK文件,反编译具体流程可看我的教程《App反编译工具dex2jar、JD-GUI的使用》。

ps:dex2jar反编译某鱼App过程可能会碰到内存溢出问题,需要把dex2jar的最大内存设置大一点;

第二步:在源码中找到x-sign生成方法

使用jd-gui工具打开反编译后的jar包查看源码;

然后使用jd-gui的搜索功能或者自己利用经验找x-sign生成方法吧,这一步会比较费时间,某鱼App的源码经过了混淆,不同版本都不同,这里我最终通过“sign”关键词搜索到了,生成方法在mtopsdk.security.InnerSignImpl类下,具体代码如下:

某鱼App加密参数x-sign生成教程

第三步:Hook

找到生成方法后,我们就可以利用Xposed尝试进行Hook了。

使用XposedHelpers中的findAndHookMethod方法对签名方法进行hook,方法名为getMtopApiSign,参数有3个,分别为HashMap、String、String;然后重写XC_MethodHook中的beforeHookedMethod、afterHookedMethod两个方法,分别对输入参数、输出结果进行打印,具体代码如下:

Class clazz = hostClassLoader.loadClass("mtopsdk.security.InnerSignImpl");
XposedHelpers.findAndHookMethod(clazz, "getMtopApiSign", HashMap.class, String.class, String.class, new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        super.beforeHookedMethod(param);
        XposedBridge.log("某鱼x-sign参数0:" + param.args[0].toString());
        XposedBridge.log("某鱼x-sign参数1:" + param.args[1].toString());
    }

    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        XposedBridge.log("某鱼x-sign结果:" + param.getResult());
    }
});

安装至手机并开启当前Xposed模块进行测试

第四步:完善远程调用签名方法接口相关

在invoke方法中完成远程调用相关代码的编写,利用反射调用签名方法,接口返回签名结果x-sign。

第五步:测试

安装HermesAgent至手机,在Xposed中开启该模块,并重启手机;

在浏览器中调用远程接口测试,效果如下:

某鱼App加密参数x-sign生成教程

HermesAgent相关可看我写的教程《HermesAgent群控系统使用教程》。

最近代码如下(最终代码中我还加了一个x-mini-wua参数的生成):

package com.virjar.hermes.hermesagent.hookagent;

import com.koushikdutta.async.http.Multimap;
import com.virjar.hermes.hermesagent.aidl.InvokeRequest;
import com.virjar.hermes.hermesagent.aidl.InvokeResult;
import com.virjar.hermes.hermesagent.plugin.AgentCallback;
import com.virjar.hermes.hermesagent.plugin.SharedObject;
import com.virjar.hermes.hermesagent.util.CommonUtils;

import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

/**
 * 支持闲鱼App 6.5.10版本
 *
 * @author HuaYunBin
 */
public class XianYuSignAgent implements AgentCallback {

    private Object signMethodObject;
    private Method signMethod;
    private Method wuaMethod;

    @Override
    public String targetPackageName() {
        return "com.taobao.idlefish";
    }

    @Override
    public boolean needHook(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        return StringUtils.equalsIgnoreCase(loadPackageParam.processName, "com.taobao.idlefish");
    }

    @Override
    public InvokeResult invoke(InvokeRequest invokeRequest) {
        String paramContent = invokeRequest.getParamContent();
        Multimap nameValuePairs = CommonUtils.parseUrlEncoded(paramContent);

        String deviceId = nameValuePairs.getString("deviceId");
        String utdId = nameValuePairs.getString("utdId");
        String ttId = nameValuePairs.getString("ttId");
        // sid可为空
        String sid = nameValuePairs.getString("sid");
        // uid可为空
        String uid = nameValuePairs.getString("uid");
        String appKey = nameValuePairs.getString("appKey");
        String t = nameValuePairs.getString("t");
        String api = nameValuePairs.getString("api");
        String data = nameValuePairs.getString("data");
        String v = nameValuePairs.getString("v");
        if (deviceId == null || utdId == null || appKey == null || t == null || api == null | data == null || v == null) {
            return InvokeResult.failed("请求参数错误");
        }

        String sign = makeSign(deviceId, utdId, ttId, sid, uid, appKey, t, api, data, v);
        String wua = makeWua(t, appKey);
        if (sign == null) {
            return InvokeResult.failed("生成Sign失败");
        } else if (wua == null) {
            return InvokeResult.failed("生成Wua失败");
        }

        Map resultMap = new HashMap(2);
        resultMap.put("x-sign", sign);
        resultMap.put("x-mini-wua", wua);
        return InvokeResult.success(resultMap, SharedObject.context);
    }

    private String makeWua(String t, String appKey) {
        HashMap hashMap = new HashMap(2);
        hashMap.put("pageName", "");
        hashMap.put("pageId", "");
        try {
            return (String) wuaMethod.invoke(signMethodObject, t, appKey, null, hashMap, 8);
        } catch (Exception e) {
            return null;
        }
    }

    private String makeSign(String deviceId, String utdId, String ttId, String sid, String uid, String appKey, String t, String api, String data, String v) {
        HashMap hashMap = new HashMap(11);
        hashMap.put("deviceId", deviceId);
        hashMap.put("utdid", utdId);
        hashMap.put("ttid", ttId);
        hashMap.put("appKey", appKey);
        hashMap.put("api", api);
        hashMap.put("data", URLDecoder.decode(data));
        hashMap.put("x-features", "27");
        hashMap.put("v", v);
        hashMap.put("sid", sid);
        hashMap.put("t", t);
        hashMap.put("uid", uid);
        XposedBridge.log("生成Sign的Map值为: " + hashMap.toString());

        try {
            return (String) signMethod.invoke(signMethodObject, hashMap, appKey, null);
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public void onXposedHotLoad() {
        ClassLoader hostClassLoader = SharedObject.loadPackageParam.classLoader;
        try {
            Class clazz = hostClassLoader.loadClass("mtopsdk.security.InnerSignImpl");
            XposedHelpers.findAndHookMethod(clazz, "getMtopApiSign", HashMap.class, String.class, String.class, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    super.beforeHookedMethod(param);
                    XposedBridge.log("某鱼x-sign参数0:" + param.args[0].toString());
                    XposedBridge.log("某鱼x-sign参数1:" + param.args[1].toString());
                }

                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                    XposedBridge.log("某鱼x-sign结果:" + param.getResult());

                    signMethodObject = param.thisObject;
                    signMethod = (Method) param.method;
                }
            });
            XposedHelpers.findAndHookMethod(clazz, "getSecBodyDataEx", String.class, String.class, String.class, HashMap.class, int.class, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    super.beforeHookedMethod(param);
                    XposedBridge.log("某宝x-mini-wua参数0:" + param.args[0]);
                    XposedBridge.log("某宝x-mini-wua参数1:" + param.args[1]);
                    XposedBridge.log("某宝x-mini-wua参数2:" + param.args[2]);
                    XposedBridge.log("某宝x-mini-wua参数3:" + param.args[3]);
                    XposedBridge.log("某宝x-mini-wua参数4:" + param.args[4]);
                }

                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                    XposedBridge.log("某宝x-mini-wua结果:" + param.getResult());

                    wuaMethod = (Method) param.method;
                }
            });
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

发布者:糖太宗,转载请注明出处:https://www.qztxs.com/archives/science/technology/5607

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022年5月7日 下午4:33
下一篇 2022年5月7日 下午10:33

相关推荐

  • MySQL主从之外,你又多了一项选择,Galera

    绝大部分互联网公司,都使用MySQL的InnoDB引擎存储数据。   为了保证数据库的高可用,为了保证性能的扩展,绝大部分公司又会使用主从同步,读写分离的MySQL集群架构。 传统的主从同步,读写分离MySQL集群架构如上图所示: (1)主库:左侧第一个实例,提供写服务的实例; (2)从库:右侧两个实例,提供读服务的实例;   此时数据复制是如何实现的呢? ...

    2022年5月11日
    1500
  • Netsparker扫描器IAST使用

    0x00 前言 之前测试了AWVS扫描器的IAST功能,使用上不是很方便,需要对每个服务启动一个扫描任务才可以进行扫描,比较主动,无法作为独立的被动式扫描器使用,脏数据也是比较多 对比awvs和netsparker,在前后端分离场景下,awvs扫描器可以分析swagger文件进行全接口自动化安全扫描,开发人员可以使用该功能增加后端系统的安全测试效率。在多人协...

    2022年6月13日
    3400
  • 为什么MySQL要升级组复制?1分钟系列

    前几天发了《Galera,MySQL主从之外的另一种选择》之后,很多朋友在评论里留言: “这不就是Oracle Rac吗?” “这不就是MGR吗?” …   思路比结论重要,为什么比是什么重要,今天就花1分钟,说下这里面架构演进的思路。 画外音:大家不想听底层细节,就不深入细节了。   最早的数据库都是单机的,其最大的痛点是啥? 无法线性扩展。   磁盘能力...

    2022年5月11日
    1700
  • java接口测试面试题

    如何部署环境? 以docker为例java语言为主: 1、程序员编写完代码之后, 2、然后编写了docker file文件, 3、我通过docker file 构建成镜像之后, 4、运行整个镜像,容器启动之后,我们的环境就部署完毕了 如果程序出错,如何定位到错误日志? 整个分为2种情况 第一种:如果整个程序是使用docker容器来部署的,运行整个程序的 ,那...

    技术 2022年5月21日
    2500
  • 零基础学Java第六节(面向对象二)

    本篇文章是《零基础学Java》专栏的第六篇文章,文章采用通俗易懂的文字、图示及代码实战,从零基础开始带大家走上高薪之路! 本文章首发于公众号【编程攻略】 继承 创建一个Person类 我们创建一个用于描述人的类。我们怎么抽象出一个人这个类呢?我们以不同的角度做抽象,得到的属性和行为都会有些区别。这里,我们主要从人的社会属性来抽象。为了表示性别,我们先顶一个枚...

    2022年5月22日
    1500

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信