Hook类中普通方法
在本文中,我们继续以某嘟牛应用为例,在用户点击登录按钮后,哪个方法会被调用。我们在上一篇文章中提到,搜索关键词 “Encrypt” 后,发现两个包含该字符串的 Java 方法。接下来,我们通过 Frida 分别 Hook 这两个方法,以测试点击登录后会调用哪个方法。以下是 Frida 脚本代码:
function HookMethod() {
Java.perform(function () {
var JsonRequest = Java.use("com.dodonew.online.http.JsonRequest");
console.log("JsonRequest = ", JsonRequest);
JsonRequest.paraMap.implementation = function (addMap){
console.log("addMap = ", addMap);
this.paraMap(addMap);
}
JsonRequest.addRequestMap.overload('java.util.Map', 'int').implementation = function (arg0,arg1) {
console.log("addRequestMap params: ", arg0, arg1);
var HashMap_arg0 = Java.cast(arg0, Java.use("java.util.HashMap"));
console.log("addRequestMap params: ", HashMap_arg0.toString());
this.addRequestMap(arg0, arg1);
}
});
}
function main () {
HookMethod();
}
setImmediate(main);
在终端中执行以下命令以注入我们的 JavaScript 代码:
frida -U -f com.dodonew.online -l .\demo.js --no-pause
执行结果如下所示:
通过结果我们可以看出,点击登录后最终执行的方法为 addRequestMap。该方法的代码如下:
public void addRequestMap(Map<String, String> addMap, int a) {
// 获取当前系统时间的时间戳,并将其转换为字符串
String time = System.currentTimeMillis() + "";
// 如果传入的 addMap 为空,则初始化一个新的 HashMap
if (addMap == null) {
addMap = new HashMap<>();
}
// 将时间戳加入到 addMap 中,键为 "timeStamp"
addMap.put("timeStamp", time);
// 调用 RequestUtil 类的 paraMap 方法,对 addMap 进行参数处理,生成一个包含签名的字符串
String code = RequestUtil.paraMap(addMap, Config.BASE_APPEND, "sign");
// 调用 RequestUtil 类的 encodeDesMap 方法,对生成的字符串进行 DES 加密,得到加密后的字符串
String encrypt = RequestUtil.encodeDesMap(code, this.desKey, this.desIV);
// 创建一个新的 JSONObject 对象
JSONObject obj = new JSONObject();
try {
// 将加密后的字符串放入 JSON 对象中,键为 "Encrypt"
obj.put("Encrypt", encrypt);
// 将 JSON 对象转换为字符串,并赋值给 mRequestBody
this.mRequestBody = obj + "";
} catch (JSONException e) {
// 捕获并打印 JSON 异常
e.printStackTrace();
}
}
协议分析
通过以上终端的输出,我们可以得知,输入的手机号码和密码并没有进行加密。因此,我们需要继续跟进代码,首先进入 RequestUtil.paraMap 方法,代码如下:
public static String paraMap(Map<String, String> addMap, String append, String sign) {
try {
// 获取传入的 Map 的键集
Set<String> keyset = addMap.keySet();
// 用于拼接字符串的 StringBuilder
StringBuilder builder = new StringBuilder();
// 用于存储键值对的列表
List<String> list = new ArrayList<>();
// 遍历键集,将每个键值对以 "key=value" 的形式添加到列表中
for (String keyName : keyset) {
list.add(keyName + "=" + addMap.get(keyName));
}
// 对列表进行排序
Collections.sort(list);
// 遍历排序后的列表,将每个键值对拼接到 StringBuilder 中,以 "&" 分隔
for (int i = 0; i < list.size(); i++) {
builder.append(list.get(i));
builder.append("&");
}
// 将追加字符串(append)拼接到字符串的末尾
builder.append("key=" + append);
// 计算字符串的 MD5 哈希值,并转换为大写
String checkCode = Utils.md5(builder.toString()).toUpperCase();
// 将计算出的签名(checkCode)放入传入的 Map 中,键为 "sign"
addMap.put("sign", checkCode);
// 将 Map 按键排序后转换为 JSON 字符串
String result = new Gson().toJson(sortMapByKey(addMap));
// 打印日志,调试用
Log.w(AppConfig.DEBUG_TAG, result + " result");
// 返回生成的 JSON 字符串
return result;
} catch (Exception e) {
// 捕获并打印异常
e.printStackTrace();
return "";
}
}
在 paraMap 方法中,调用了 Utils.md5 方法(调用 Utils.md5 之前都是明文)。为了了解传入的值如何进行 MD5 加密,我们需要 Hook 这个方法,查看传入的数据和加密后的结果,代码如下:
var utils = Java.use("com.dodonew.online.util.Utils");
utils.md5.implementation = function (arg0) {
console.log("md5 params: ", arg0);
var retval = this.md5(arg0);
console.log("md5 retval: ", retval);
return retval;
}
运行结果如下:
通过测试可知,Utils.md5 为标准的 MD5 算法。
在执行完成 RequestUtil.paraMap 方法后,返回的为 JSON 格式的字符串。接着我们 Hook RequestUtil.encodeDesMap 方法,代码如下:
var RequestUtil = Java.use("com.dodonew.online.http.RequestUtil");
RequestUtil.encodeDesMap.overload('java.lang.String', 'java.lang.String', 'java.lang.String').implementation = function (arg0,arg1,arg2) {
console.log("encodeDesMap arg0: ", arg0);
console.log("encodeDesMap arg1: ", arg1);
console.log("encodeDesMap arg2: ", arg2);
var ret = this.encodeDesMap(arg0,arg1,arg2);
console.log("ret = ",ret);
return ret;
}
此时我们可以看到传入的三个参数,如下图:
至此我们已经看到了输入的明文账号和密码,以及加密后的数据。