博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android调试H5
阅读量:5787 次
发布时间:2019-06-18

本文共 4306 字,大约阅读时间需要 14 分钟。

hot3.png

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

你可能感兴趣的文章
Leetcode - Permutations I,II
查看>>
centos下安装JAVA开发工具(1)------JDK
查看>>
如何使用@vue/cli 3.0在npm上创建,发布和使用你自己的Vue.js组件库
查看>>
Luakit的前世今生
查看>>
linux命令最终篇
查看>>
Node.js 线程你理解的可能是错的
查看>>
别把机器学习和人工智能搞混了!
查看>>
jenkins
查看>>
Spring 冬天来了,春天还远吗
查看>>
Git版本管理工具常用命令整理和说明
查看>>
Web SCADA 电力接线图工控组态编辑器
查看>>
“音”你而来,“视”而可见 腾讯云+社区音视频技术开发实战沙龙圆满结束...
查看>>
LeetCode Most Common Word 最常见的词
查看>>
在一头扎进机器学习前应该知道的那些事儿
查看>>
logstash mysql 准实时同步到 elasticsearch
查看>>
Python 内存管理
查看>>
node.js学习之npm 入门 —8.《怎样创建,发布,升级你的npm,node模块》
查看>>
Haproxy反向代理WebSocket的方法
查看>>
【读书笔记】A Swift Tour
查看>>
canvas绘制视频封面
查看>>