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

60次阅读
没有评论

通过对某鱼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<String, Object> 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<String, String> 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<String, String> 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();
        }
    }
}

评论(没有评论)
载入中...