好友
阅读权限 20
听众
最后登录 1970-1-1
本帖最后由 szh12123 于 2026-6-4 23:51 编辑
本文章中所有内容仅供学习交流使用,不用于其他任何目的,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!若有侵权,请联系作者删除。
每过一段时间就要奖励自己一个逆向分析,分析完这一个网址,感觉自己的逆向水平大概算得上入门了吧,虽然比较费时间,但好在最后是成功了,现在把详细分析过程发出来,相互学习交流;
网址地址: aHR0cHM6Ly9wYXNzcG9ydC56aGlodWlzaHUuY29tL2xvZ2lu
打开浏览器调试,刷新,随便输入账号密码,出现滑块验证,验证成功后,查看数据提交过程,最终锁定了几个关键网址:
https:// xxxxx/api/v2/getconf
https:// xxxxx/v4/j/up
https:// xxxxx /api/v3/get
https:// xxxxx /api/v3/check
https:// passport.xxxxx.com/user/validateAccountAndPassword
###1.先看第一个 https://xxxxx/api/v2/getconf
直接搜索关键字 getconf很容易找到参数赋值的代码,
打断点调试发现, R就是其中 4个参数,这里我直接用这 4个参数请求了一下,
[Python] 纯文本查看 复制代码
url = "https://xxxxx/api/v2/getconf"
params = {
"id": "75f9f716460a422f89a628f50fd8cc2b", #initNECaptcha中的预设值captchaId
"ipv6": "false", # 未定义
"runEnv": "10", # 预设值 WEB: 10,
"iv": "5", # 预设值 v.IV_VERSION
}
response = requests.get(url, params=params)
出现了返回值为 callback isinvalid ,然后搜 JSONP直接就找到参数生成的位置:
var l,p, f = Math.random().toString(36).slice(2, 9), h = a.prefix ||"__JSONP", d = a.name || h + ("_" + f) + ("_" +i++), v = a.param || "callback", y = a.timeout || 6e3, m = window.encodeURIComponent,g = document.getElementsByTagName("script")[0] || document.head;
简化一下就是 d ="__JSONP"+ ("_" + Math.random().toString(36).slice(2, 9)) +("_" + i++) 这里的 i是当前网址的请求序号,从 0开始,所以取 0;
使用 python编程:
[Python] 纯文本查看 复制代码
import random
import string
I = 0
def callbak():
global I
m = ''.join(random.choices(string.ascii_lowercase + string.digits, k=7))
result = "__JSONP" + "_" + m + "_" + str(I)
I += 1
return result
params = {
"id": "75f9f716460a422f89a628f50fd8cc2b", #initNECaptcha中的预设值captchaId
"ipv6": "false", # 未定义
"runEnv": "10", # 预设值 WEB: 10,
"iv": "5", # 预设值 v.IV_VERSION
"callback": callbak() # 随机值
}
直接请求成功:
__JSONP_ztyfoeu_0({"data":{"dt":"W93/vPVL……..net"]},"error":0,"msg":"ok"});
好像不需要 referer、 zoneId和 loadVersion, “referrer”就是网站网站址, “zoneId”为空不用管,
我还是找了一下 "loadVersion":"2.5.4", 是预设值代码赋值 r.loadVersion= t._captchaConfig.loadVersion;
第一个网址分析到此结束
====================================================================================================
###2.接下来是这个网址 https://xxxxx/v4/j/up
总共 5个参数,首先可以看到 p在上一个网址请求的结果中,
p = result["data"]["ac"]["pn"],
这里搜 up显然不管用,搜 yanzhengma也没戏,搜了下 vk,很容易找到参数位置,
还原一下代码
[Python] 纯文本查看 复制代码
p = a = (h = n["context"])["appId"]
vk = s = h["versionKey"]
v = O = h["sdkVersion"]
O和 s感觉像是预设值,然后我根据调用堆栈找了一下源头,发现最开始赋值的位置,
逐一进入函数发现各个参数的赋值:
还原一下看的更清楚,
[JavaScript] 纯文本查看 复制代码
function n(n) {
var t = {};
t['cacheKey'] = 'ntes_utid',
this['appId'] = n,
this['collectDuration'] = 0,
this['visitDuration'] = 0,
this['nonce'] = "",
this['accessInfo'] = 'init:1-gts:1',
this['timestamp'] = "",
this['visitTime'] = O(),
this['collectTime'] = 0,
this['tkCacher'] = new hu(t),
this['cacher'] = new hu({
cacheKey: this['getCacheKey'](),
simple: !0
}),
this['sdkVersion'] = '2.0.13_yanzhengma',
this['versionKey'] = 'd44593ca',
this['sdkType'] = 1
}
这里的 n就是上一个网址提取的 result["data"]["ac"]["pn"],然后 vk和 v参数也知道了是预设值;剩下 n=G(), d=v(),
直接打断点然后控制太看 G的函数位置,得到 G():
[JavaScript] 纯文本查看 复制代码
function G() {
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'['replace'](/[xy]/g, function(n) {
var t = 16 * Math['random']() | 0;
return ("x" === n ? t : 3 & t | 8)['toString'](16)
})
}
Python写法如下:
[Python] 纯文本查看 复制代码
import random
def G():
def replace_char(n):
t = int(16 * random.random())
return format(t if n == 'x' else (3 & t | 8), 'x')
return ''.join(
replace_char(c) if c in 'xy' else c
for c in 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'
)
d=v()也是同样的方法,稍微还原简化后也就 300行,所以这个解析的过程我帮大家跳过了,我就把主代码放上来吧,
[Python] 纯文本查看 复制代码
def v(n):
t = N('fd6a43ae25f74398b61c03c83be37449')
r = [Z(math.floor(256 * random.random())) for i in range(4)]
t = A(R(t),R(r))
t = R(t)
i = [t, r]
u = i[0]
e = i[1]
c = N(crc(n))
def inner_func(n):
if not len(n):
return l(64, 0)
t = []
r = len(n)
i = 64 - r % 64 - 4 if r % 64 <= 60 else 128 - r % 64 - 4
P(n, 0, t, 0, r)
for u in range(i):
while len(t) <= r + u:
t.append(0)
t[r + u] = 0
P(p(r), 0, t, r + i, 4)
return t
def outer_func(n):
if len(n) % 64 != 0:
return []
t = []
r = len(n) // 64
u = 0
for i in range(r):
t.append([])
for e in range(64):
t[i].append(n)
u += 1
return t
o = outer_func(inner_func(n+c))
f = [] + e
v = u
m = 0
h = len(o)
while m < h:
def func(n):
t = [q, K, x, V, Y, W, H]
r = '037606da0296055c'
i = 0
u = len(r)
while i < u:
e = r[i:i + 4]
c = B(e[0:2])
o = B(e[2:4])
n = t[c](n, o)
i += 4
return n
def func1(n, t):
i = len(t)
e = len(n)
r = []
for u in range(e):
r.append(Z(n + t[u % i]))
return r
a = A(func(o[m]), u)
a = A(func1(a, v),v)
v = S(S(a))
P(v, 0, f, 64 * m + 4, 64)
m += 1
return M(f,list('MB.CfHUzEeJpsuGkgNwhqiSaI4Fd9L6jYKZAxn1/Vml0c5rbXRP+8tD3QTO2vWyo'), "7")
我们来分析一下它的参数,它传入的参数是一个列表,
意思是 t+o+f的列表, o和 f的赋值在 v=前面有:
[Python] 纯文本查看 复制代码
c = m['sent']()
o = c[0]
f = c[1]
所以要知道 c值,然后看到 m函数里面,
往上翻一下发现 e的赋值跟 r有关,而 r是传入的参数
然后走一遍看 e是如何生成的,接着 根据调用堆栈一路往上追到了这里,发现到头了;
f的值是 o[“value”],而 o是 c的 next下一个,然后就找不到这个数组从哪来的了,但多次调试发现他是预设值,所以大概是初始化的时候就已经生成了;然后我某次刷新的时候发现断在这里的 r运行多次后会有变化,不知道是多少次,反正挺多的;
然后这里的值函数内运算后会变成数组,然后这个数刚好就是 Array(75)其中的一个,就是这么幸运,
所以接下来在这里单步调试,发现是在这行代码中变化,
单步进去看到这里有个变换,
然后看到上面闭包有三个参数,于是搜了一下 t的值 254,
结果发现这里三个字刚好对应 n,t,r;
然后在这里打上断点刷新看他怎么走的,这里确实有点烦人,又是异步又是混淆,好在经过我的不懈努力,最后把代码简化成这样:
[Python] 纯文本查看 复制代码
# 关键函数简化后
def L(n, t, r):
u = r(0, n())
p_t = p(t)
p_len = p(len(u))
return p_t[-2:] + p_len[-2:] + list(u)
# 测试1
def Vt(): # 这个是传入的第一个参数,只需要返回值
return 2
def tn(n,t,r):
def function(n, t):
if n == 0:
if t:
temp = 1
else:
temp = 2
else:
temp = n
return D(p(temp), r)
result = L(n, t, function) # 虽然都是调用L,但每个function都不一样
print(f"测试结果:{result}")
tn(Vt, 218, 1) # 三个参数对应
# 测试2
def jn(): # 这个是传入的第一个参数,只需要返回值
return '48000,2,1,0,2,explicit,speakers'
def un(n,t,r):
def function(n, t):
if n == 0:
return D(N(t), r)
else:
return D(N(""+n), r)
result = L(n, t, function) # 虽然都是调用L,但每个function都不一样
print(f"测试结果:{result}")
un(jn,252,100) # 三个参数对应
随便测试了两个用例发现没有问题,传入参数需要用到的其他函数需要单独扒;由于这个 Kr()里面数组太多了不想一个一个简化分析,不过大致思路都差不多。 分析完 Kr然后我们回到之前 t+o+f的列表,发现其实出发点有点问题,但结果好像不差,其实这个 Kr赋值给了 t,而 o和 f是 Kr下面两个 Vr和 Wr的值,稍微还原一下第一个参数值,就能发现 Vr中的第一个参数就是之前还原代码 n(n)中赋的值, Wr是鼠标滑动相关的值; 然后我们来看 Wr:因为他是混淆后的代码,所以直接搜混淆,找到了这里,
还原一下看的更清晰:
[JavaScript] 纯文本查看 复制代码
function n() {
this['moveCount'] = 0,
this['clickCount'] = 0,
this['downCount'] = 0,
this['upCount'] = 0,
this['motionCount'] = 0,
this['orientationCount'] = 0,
this['keypressCount'] = 0,
this['focusCount'] = 0,
this['blurCount'] = 0,
this['scrollCount'] = 0,
this['popstateCount'] = 0,
this['trustedCount'] = 0,
this['unTrustedCount'] = 0
}
13个参数刚刚好,而且这个 Gu很熟悉啊,不就是前面 Wm代码里的嘛,创建监听鼠标然后执行开始,后面还有个执行结束,
这里就凑齐了所有参数了,经测试发现全部为零也没有任何影响,我就直接拿生成好的三个列表展平为一维列表传入事先还原好的 v()中,检查一下 v函数有问题没,
成功获取!
{"code":200,"data":{"ed":"","ed2":"","es":"b32833xxxxx9def4c7","td":"bLR+GSxxxxxlzISnT1","tk":"c1lrkAxxxxxo5EstI","ts":178xxxxx196},"msg":"成功 ","ok":true}
====================================================================================================
###3.终于到第三个了 https:// xxxxx/api/v3/get先看参数,这里这个 referer其实一直没啥用,然后很多老熟人,
那就随便搜一个把 'sdkVersion',找到一个相似度最高的;为啥不搜混淆后的 this[y(724)]呢,因为已经搜过了就只有之前那一个;
id是之前分析过了, zonId和 dt在 getconf的返回值中和, irToken在 up返回值,所以来看 fp,发现他是 fingerprint赋值,然后搜索 fingerprint,找到了他赋值的位置,
然后就找不到这个参数从哪来的了,几经周折发现他在 cookie中自动就生成了,实在是找不到地方,于是就解锁了新技能 hook大法,
使用事件监听器断点,在控制台输入下面的代码,
[JavaScript] 纯文本查看 复制代码
(function() {
'use strict';
Object.defineProperty(window, 'gdxidpyhxde', { // 修改goldlog为你需要查询的window属性
get: function() {
// debugger;
return "";
},
set: function(value) {
debugger;
return value;
},
});
// Your code here...
})();
最终获得了值的来源;
好了又是繁琐的分析阶段,跳过分析,结果就是元素参数就 4个,只有 u是变化的,对这个用了一系列加密,最后结果再拼接一个时间戳,
[Python] 纯文本查看 复制代码
dict1 = {
'v': 'v1.1',
'h': 'passport.xxxxx.com',
'u': _0x3f6f3f(3) + str(int(time.time() * 1000) + 900000) + _0x3f6f3f(3),
'fp': '418xxxxx53,3610xxxxx3142',
}
===省略===
result = ''.join(chunks)
final_time = int(time.time() * 1000) + 900000
window['gdxidpyhxde'] = f"{result}:{final_time}"
fp结束再看 cb, cb也是一抹多混淆,但在我解的过程中越看越熟悉,这不就是之前的 v()嘛,然后回过头一看 fp的核心加密方式也是一样的,不知道为啥都用同一种加密方式, cb就用现成的,
[Python] 纯文本查看 复制代码
def cb():
code = 'vfnv46'
pos = [1, 10, 12, 13, 26, 31]
uuid = _0x2b4c4d(32)
uuid = list(uuid)
for i in range(len(pos)):
uuid[pos[i]] = code[i]
return vv.v([ord(i) for i in uuid]) #这里调用的之前的v()
def _0x2b4c4d(n):
m = list('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
t = len(m)
rt = [m[int(random.random() * t)] for i in range(n)]
return ''.join(rt)
print(cb())
其他参数不用怎么管,比较常规,然后测试一下,
请求成功!
====================================================================================================
###4.接下来是这个网址 https:// xxxxx /api/v3/check
dt、 id已知, token是 get返回值, cb后面查看发现调用的同一个函数, callback最开始分析过了,主要就是 data中的几个参数,然后这里多了个 extraData的参数,那就直接搜这个,直接就定位到了这里,
查看堆栈找到了 data的几个参数,
参数源头都在上面,需要一层一层的分析函数,比较繁琐,经分析得知,
m为空, d、 p、 f、 ext最外层加密一致,他们参数不一样,
d主要是两个参数 token之前 up获取的返回值,和单组鼠标轨迹的列表加密,
f的一个参数是 token,另一个参数是鼠标轨迹列表,每组列表由四个参数构成,也要套加密,
模拟了一个鼠标轨迹,
[Python] 纯文本查看 复制代码
X_end = int(input("输入X轴偏移量:"))
st = random.randint(60, 90)
tm = []
length = 0
dragX = []
X_init = 0
while True:
temp = random.randint(2, 7)
X_init += temp
st += random.randint(6, 9)
tm.append(st)
dragX.append(X_init)
X_end -= temp
length += 1
if X_end < 0:
dragX[-1] += X_end
break
dragX.sort(reverse=True)
Y = [random.randint(0, 10) for i in range(length)]
Y.sort()
traceData = []
mouse_list =[]
for i in range(length):
m_da = [dragX[i], Y[i], tm[i], 1] #[拖拽X偏移距离,拖拽Y偏移距离,距开始的时间差,真实判断]
mouse_list.append(m_da)
traceData.append(p_data(token, ",".join(map(str, m_da))))
p是 token和滑块 X轴偏移的像素,需要通过图片识别得到,图片识别就直接跳过了,本来想训练个大模型识别,奈何自身的技能点数不够,所以后面直接获取图片人工测试;
[Python] 纯文本查看 复制代码
token, str(X_end / 320 * 100)
ext是 token和 ”鼠标按下次数 ”+“,”+”轨迹列表长度 ”的拼接,套个加密; 好了参数全了,最后测试一下,完美!
====================================================================================================
###5.最后一个就不那么难了, https://passport.xxxxx.com/user/validateAccountAndPassword
参数就一个 secretStr,
就一行,是不是过于简单了点,
转为 python实现:
[Python] 纯文本查看 复制代码
import base64
from urllib.parse import quote
word = {"account":"xxx","password":"xxx","validate":"CN31_b.xxxxx7_v_i_1"}
word = json.dumps(word,separators=(',',':'),ensure_ascii=False)
encoded = quote(word, safe=':,')
secretStr = base64.b64encode(encoded.encode()).decode()
其实 word里面还有 validate一个加密参数,搜索关键词通过堆栈跟,还是不难找,
三个参数,第一个是返回的 validate,第二个是之前的 fp,第三个 zoneId,然后核心还是同一个加密函数,但凡多搞几种都没有心情分析了,整体连贯起来尝试,
登录成功!以上就是全过程了!