Android调试H5

android怎么调试已经上线的H5呢? 这里以某APP为例, 展示了如何HOOK WebView, 以及如何调式JS加密算法

准备工作

在android中, 一般对H5进行调试, 是打开chrome 输入 chrome://inspect/#devices 然后选择调试的app内嵌网页即可 Alt text 但是对于已经发布的release版本, 如果Webview未开启调试, chrome是看不见可调试H5的

具体API

android.webkit.WebView.setWebContentsDebuggingEnabled(boolean enabled)

对于release的app, 一般有几种方式打开这个标识

  • app增加设置功能
  • 修改smail代码二次打包
  • 通过xposed hook api 三种方案难易程度来看, 第三种最难, 那我们就直接上最难的方式, 本次案例以某友商电商app为例

笔者这里选择pixel进行root&刷入xposed框架. Hook的api

   Class clz = loader.loadClass("android.webkit.WebView");
   Method method = clz.getMethod("setWebContentsDebuggingEnabled", boolean.class);
   method.invoke(clz, true);

}

loader传入为hook app的classloader 这里如果app<font color=Red>**有壳 **</font>, <font color=Red>**有壳 **</font>, <font color=Red>**有壳 **</font>重要的事儿说三遍, 还需要绕一下~

思路是先hook ClassLoader

XposedHelpers.findAndHookMethod("java.lang.ClassLoader", loader, "loadClass", String.class, Boolean.TYPE, this);

然后在回调hook相关的class

 @Override
    protected void afterHookedMethod(MethodHookParam param) {
			...
			Class = (Class) param.getResult();
            ClassLoader tmpLoader = loadClass.getClassLoader();
            Class cls = null;
            try {
                cls = XposedHelpers.findClass(clsName, tmpLoader);
            } catch (Throwable e) {
                e.printStackTrace();
            }
            if (cls != null) {
            //your code
            }
            ....
       }

Hook完成之后, 打开相关H5, 在chrome就能看到inspect选项啦~ Alt text

分析点

so, lets begin 分析接口算法 我们要分析的接口如下 ![Alt text](https://haitao.nos.netease.com/9ee8bf01-b8ba-4511-95ab-6e0c321de7e8_2252_1338.jpeg) 主要是探究这个dfpToken是如何生成的, 这里我们提取关键字getDrip.do` 下面分析会用到 在chrome点击inspect, 会打开一个新窗口, 第一次可以cmd+R 进行reload一次(加载H5源码)

然后分析H5的框架层, 加载了哪些JS Alt text

在上图的的js文件里面, 搜索到了关键字getDrip.do

断点1

Alt text

HttpPostAsync("/m/newsign/getDrip.do", {
   dfpToken: window.riskManage.getDfpToken(),
   channelType: window.myDevice
   }).catch(function(t) {
   Et.enQueue(X.GetDripTimeOut)
 });

从上面代码中, 知道是一个POST请求, path是/m/newsign/getDrip.do, 参数有dfpToken & channelType 其中dfpToken是调用riskManage.getDfpToken(), 这里是我们希望分析的 因此在代码中dfpToken: window.riskManage.getDfpToken()这里我们进行分析. 在console里面输入 window.riskManage回车, 看看这个riskManage到底是个什么鬼~ Alt text 这里依次展开定位为index.min.js

断点2

这里js方法为 Alt text

  key: "getDfpToken",
  value: function() {
  if (window._dfp && window._dfp.getToken)
      return _dfp.getToken();
      s.error("getDfpToken() error: no _dfp or _dfp.getToken")
  }

继续分析window._dfp.getToken, 定位到下一段JS代码

断点3

Alt text 如上图我们在断点3下断. console里面执行window._dfp.getToken(), 会在断点3暂停

在断点3处, 我们继续输入E, p() 发现结果如下: Alt text

分析到E是内存缓存, p()是真正生成Token的方法, 因此每次在p()下断的时候, 需要在console把E手动置为undefined

断点4

在console, 输入p, 点击输出的函数, 可定位到函数p() 并且下断设为断点4 Alt text

在console我们先执行E = undefined , 然后继续执行到p函数 执行SY0,qsK,Shl等对象, 发现console输出的都是Xu2对象, 在Xu2对象里面, 找到toString()方法如下

!(SY0 = (n1p[(n1p.toString = function() {
           return d7c(this.s)
}
断点5

在断点5进行下断 Alt text 继续执行到断点5 执行命令d7cd7c函数下断如下 Alt text 继续执行, 这里大致格式化了下函数如下


function(n, r, t, o) {
  if (C[n]) return C[n];
    r = ""
    t = NiV0(n).fb5$(lMWq[0]).split("")
    for (o = 0; o < t.length; o++) {
     r += i.charAt(e.indexOf(t[o]));
    }
    return C[n] = r
}

对其中NiV0(n).fb5$(lMWq[0])进行初步判断是一个无意义函数, 增加阅读难度而已

NiV0(n).fb5$(lMWq[0]) -> "0x0Io" (n='0x0Io')
NiV0("netease").fb5$(lMWq[0]) -> "netease"
NiV0(".*&").fb5$(lMWq[0]) -> ".*&"

其中e, i都是常量

e -> "VF=hydBigRU9.fAOS/&p4_qawLsG1rmtnck leZJ-j?P5T6EWbu7MDzI3v2,8KXCQHoN0x:Y"
i -> "- ,:&=?zxwvqkjZYXWVUSRQPONMLKJGFDA9876543210.HT_hCbusfiadIyBnmEeg/lctorp"

因此上面的函数也就是解混淆的过程. 再次回到断点4 去混淆的代码翻译如下:

function p(n){
    var minddle = inner()
    return "TH"+ minddle + MD5(middle).substr(0, 4);
}
function inner(){
    var r = 19;
    var t = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
    var o = new Date().getTime().toString(16);
    var i = 8;
    if (i >= r)
        return o.substr(0, r);
    var C = [];
    for (e = 0; e < i; e++)
        C[e] = t[0 | Math.random() * t.length];
    if (4 <= i)
        return C.slice(0, 4).join("") + o + C.slice(4).join("");
    return C.join("") + o
}

这段函数的大致思路是

生成25位定长字符串,
格式为TH+$middle$+MD5($middle$).substr(0, 4)
middle = 随机4位+16进制时间戳+随机4位

p函数解析完毕, 回到断点3, 继续分析

函数大致还原如下

function(n) {//n = undefined
    E || (E = p());
    try {
        M < ++t && (E = p(), _fp.collect && _fp.collect(E))
    } catch (r) {

    }
    return typeof n === "function" && n(E), E
}

这里简单理解就是判断是否存在E存在直接返回, 不存在调用p函数生成dfpToken

至此, js算法还原完毕.

转载于:https://my.oschina.net/u/2462463/blog/3021696

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值