android混淆代码断点调试,Android调试H5

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

准备工作

在android中, 一般对H5进行调试, 是打开chrome 输入 chrome://inspect/#devices 然后选择调试的app内嵌网页即可

d6ea79c7-4bce-4727-a46a-2f79f455e6b5_1334_676.jpeg 但是对于已经发布的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**有壳 **, **有壳 **, **有壳 **重要的事儿说三遍, 还需要绕一下~

思路是先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选项啦~

edbb0760-08d0-491e-9a87-2a1e8dbcbd94_800_262.jpeg

分析点

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

8b0e6fd0-a5c3-4b0a-9c44-162dee4ba2cb_2222_1118.jpeg

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

断点1

b3c88166-353f-41e9-bcab-8ca93a926a2d_1552_778.jpeg

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到底是个什么鬼~

5a4b240f-86e6-48e7-baa3-d630836473db_1538_680.jpeg 这里依次展开定位为index.min.js

断点2

这里js方法为

f0d83e66-d217-425c-85af-d5c78df444d4_2318_1270.jpeg

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

b0a2d94a-08f7-4c73-8ba2-a5d6e42e8ea2_800_177.jpeg 如上图我们在断点3下断. console里面执行window._dfp.getToken(), 会在断点3暂停

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

800ecd17-0312-4cdb-83a9-e304599d6320_1754_1278.jpeg

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

断点4

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

033e8774-8fbd-4b9b-8a88-95bc17694473_2314_1450.jpeg

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

!(SY0 = (n1p[(n1p.toString = function() {

return d7c(this.s)

}

断点5

在断点5进行下断

6267fd2d-1182-46fa-9ae6-2180ba1415be_800_109.jpeg 继续执行到断点5 执行命令d7c 在d7c函数下断如下

541d8e0f-35ac-4806-a9cd-14bfbf6068a6_2238_978.jpeg 继续执行, 这里大致格式化了下函数如下

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算法还原完毕.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值