声明
本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!
目标网站
aHR0cHM6Ly9wYXNzcG9ydC5qZC5jb20vbmV3L2xvZ2luLmFzcHg/UmV0dXJuVXJsPWh0dHBzJTNBJTJGJTJGd3d3LmpkLmNvbSUyRg==
前言
上次介绍了H5st的加密。这次来研究一下模拟登陆。即登录+滑块 这里先贴一张图 下图所示 是登录要的所有参数。至于所得所有参数 怎么来的? 如何来的?请往下看。
有能力的同学可以加下本人星球哦
本文详细原文
接口分析
参数过多。这里直接步入正题。 首先打开一个无痕浏览器(这很重要) 关注一下这个slide/g.html
请求。 发现这里只有一个 e 请求是会改变的。但是好像写死也没问题。
如下图然后往后看有很多个请求。但请求都大同小异啊。这里不卖关子了。请求是绑定的鼠标事件。每次移动鼠标都有请求发过来。 进栈点看看。 这里发现d的结果已经出来 往上走。 这里发现 C(a)
的值就是生成d
的参数。 这里 传参a
为 c函数如下 这里我扣下来了。
js
C = function (a) {
if (void 0 == a || null == a)
return null;
var b = "{", e;
for (e in a)
b += "'" + e + "':",
b = "string" == typeof a[e] ? b + ("'" + a[e] + "'") : b + a[e],
b += ",";
b = b.substring(0, b.length - 1);
return E(b + "}")
}
E = function (a) {
this.tdmovebit = function () {
var a = 10
, b = 20
, c = 30;
++a;
a++;
a = ++a + ++b + c++ + a++;
return d + a - 76
}
;
var b = "23IL";
a = encodeURIComponent(a);
var e = ""
, c = "";
b += "<N01c7KvwZO56RSTAfghiFyzWJqVabGH4PQdopUrsCuX*xeBjkltDEmn89.-";
var d = 0;
var g = "";
do {
var h = a.charCodeAt(d);
d = this.tdmovebit(d);
var f = a.charCodeAt(d);
d = this.tdmovebit(d);
e = a.charCodeAt(d);
d = this.tdmovebit(d);
var k = h >> Math.round(((19 << 43) / 90 ^ 34) / 214);
h = (h & 3) << 4 | f >> 4;
var l = (f & 15) << 2 | e >> 6;
c = e & Math.round(((19 << 43) / 90 ^ 34) / 6) - 4;
isNaN(f) ? l = c = Math.round(((19 << 43) / 90 ^ 34) / 6) - 3 : isNaN(e) && (c = Math.round(((19 << 43) / 90 ^ 34) / 6) - 3);
g = g + b.charAt(k) + b.charAt(h) + b.charAt(l) + b.charAt(c);
k = h = l = c = h = f = e = ""
} while (d <= a.length);
return g + "/"
}
let a = {
"bizId": "passport_jd_com_login_pc",
"elementId": "loginname",
"seq": "sniffV",
"sessionId": "294576929414652485",
"special": "0",
"sp": 1,
"version": "1.0",
"val": "",
"ctime": 1709542681074
}
console.log(C(a));
拿到这个 d
值了。 这里继续往下 下断点。 然后我们移动鼠标就会有请求发过来。 发现这个地方 就是监听鼠标的事件。
这里不扯这个了。继续回头来走这个流程 然后我们输完密码 发现又有个 slide/s.html
请求 然后它的返回值是fail
意思是滑动失败 然后随后又发了个slide/g.html
请求。 那这样的话。我们是不是可以简单梳理一下。
- g请求拿验证码
- s请求返回结果
那这个g请求中的e
是什么? 我们找一下 第二个堆栈 那这个方法中如何获取eid
呢 我们继续追栈进去 继续进这个 a = getJdEid()['eid']
好了 进去一个虚拟机了 然后发现 这个 eid
居然在这个fcf.html 请求中 然后要访问这个请求 需要三个加密方法。 分别为 a
, g
, d
.
嫌麻烦可以把 e
参数写死 能不能成功我就不知道了。
fcf接口加密分析
这里不废话了。打断点 开干。 这里不知道怎么找。直接追栈吧。就直接从最上面的栈追。然后一步一步往上看。 如下图所示 就是加密的地方。
这里我们一个一个看把
z(a)参数分析
如上图 传递了很多参数。其中要注意的是 fc fp jtb
这里其实可以全拉下来补环境。但是我感觉有点麻烦了。 可以一个一个去扣下。 这里tdencrypt
函数很好扣。 如下图所示 这里抠出来了。但是传参我们还没扣。 fc fc是eid 测试下来可以为空; **fp ** fp 如下图 继续往上追 然后我们全局搜索_JdJrTdRiskFpInfo
然后我们进去get这个方法 把这个方法全扣下来 然后补一下环境。 ok 这样就出结果了 jtb jtb就是jd_shadow__ 这里往上找 终于找到加密的地方是个自执行函数。用了什么加密就不说了 一目了然 这里给他扣下来。发现扣下来执行结果是undefined 这里有两种结果。第一种是异常了。第二种是加密的值就是undefined 这里发现 加密了r值 而r一开始是个数组,添加了一些数组然后拼接成字符串。 然后修修补补 最后修改一下 运行就行。
n(g,d)参数分析
如上图 g 和 d 参数都被赋值给了 n。 g n.g = td_collect.tdencrypt(u); 这里就直接伪造u就可以了 tdencrypt上文 搞z的时候已经写好了。
d n.d = _JdJrRiskClientCollectData;
但是 td_collect.collect()
这个 应该是很多环境检测。我们进去看看。 至于我为什么笃定是环境检测。因为上一篇h5st中collect函数也是。 如下图 是collect() 函数。这里我们直接滑到最低。 直接把g全弄下来构造环境即可。 至此 a
, g
, d
全部搞定。全部搞定就可以获得eid了。
这里扣不扣代码和补环境差不多 都是两千多行。 这部分的代码 我放到星球里了。
滑块分析
s请求。 这里我们先看 s
请求。 这里我们需要逆向的参数是 d
其他的值 有的是通过 g
有的是可以写死。 这里 o
是用户账号 但是密码不在这个请求里。
这里我们直接进 下图这个栈 通过这个栈跟。
然后到达 submit 函数 继续追 到 a['getCoordinate'](b)
这个地方就是生成d的地方。其他值 生成的逻辑大家也可以看看。 这里我们发现 还是没有发现密码的参数是哪个? 这里可以把 这个函数抠出来。 这个抠出来的逻辑 巨!!!简单。不说了 如下图所示
g请求 搞完了s 我们来继续请求g
这里之所以倒退 主要是其他值生成的逻辑 我们可以提前看看。就可以找到要拿哪些需要的参数了 先访问这个请求。 这里值得注意的是这个 challenge
是验证码ID 也需要提出来。 其他的参数更简单了 提取出s需要的参数就行了。
密码加密
但是这里有另一个问题。 s
请求和 g
请求都搞了。但是我们一直没找到密码的加密参数啊 这里重新走一遍流程。 这里发现,我们鼠标即使不动。在输入密码的时候 依然有很多请求发过去。那这个是怎么回事? 这里继续在这个C(a)这个地方打断点。并且埋点,看看整体的逻辑。
js
console.log(a,">>>>>",C(a));false
这里为什么这么写就不用多说了 。之前xhs写过了。
这里 elementId: "nloginpwd" 暴露了这是密码加密的地方。并且传递密码还是通过每次的键盘事件。 但是这每次传。总不可能服务器接受这么多次吧。肯定还有个统一提交的地方。
登录提交
我们完整的再走一次流程。把滑块也给滑过去。 此时发现一个请求uc/loginService
这个请求应该大概率是提交的地方。
这里我们再走一下流程搜索 aksParamsU
和 aksParamsB
如下图。 aksParamsU
aksParamsB
这里aksParamsU
和 aksParamsB
发现都是公用的encrypte
函数。 这里我也复制下来了
js
function encrypte(data) {
if (checkInitStatus() && !isBlank(data)) {
try {
// const data_ = decodeURIComponent(data)
return encodeURIComponent(SummerCryptico.encryptData(publicKey, data + ""))
} catch (e) {
return data
}
} else {
return data
}
}
扣下来大概两千多行。
这里可以发现 除了常规加密 还带了一个 publicKey
这里public的生成经过断点分析后可知由最开始初始化的init请求
提供。
这里我们把data 拷贝下来分析一下。 这个参数还挺多的。 这里往上找堆栈 (发现和上文我们拷贝的参数是一样的。)
这里继续往这个getEntryptPwd下走 这里在走断点的时候发现 文中的 publickey如下所示 然后把函数扣下来加密 就是密码的加密了。 大概随便补一下就出来了。
总结
很好 这里该解密的都解密的都解密出来了。 这里简单叙述一下流程 其实就三个请求。
- g请求拿到验证码 a. 但是 e呢 需要访问fcf请求 b. fcf请求需要逆向e,g,d
- s请求根据g请求返回结果 a. e参数同上 b. d参数是通过
JDJRValidate.getCoordinate
加密轨迹得到。 - loginService请求 发起请求。并且整个从参数加密 a. aksParamsU 与 aksParamsB都是统一加密方式。不同的是加密的明文 b. 明文的密码 又是一段加密代码。
结语
本文关于代码部分的加密参数。可以看下文详解。