阅读量:0
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; }
此时我们可以看到传入的三个参数,如下图:
至此我们已经看到了输入的明文账号和密码,以及加密后的数据。