吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 82|回复: 0
上一主题 下一主题
收起左侧

[Web逆向] 某网站登录及拼图滑块参数逆向分析

[复制链接]
跳转到指定楼层
楼主
szh12123 发表于 2026-6-4 23:09 回帖奖励
本帖最后由 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"});
好像不需要refererzoneIdloadVersion“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"]


Os感觉像是预设值,然后我根据调用堆栈找了一下源头,发现最开始赋值的位置,




逐一进入函数发现各个参数的赋值:

还原一下看的更清楚,
[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"],然后vkv参数也知道了是预设值;剩下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的列表,of的赋值在v=前面有:

[Python] 纯文本查看 复制代码
c = m['sent']()
o = c[0]
f = c[1]


所以要知道c值,然后看到m函数里面,

往上翻一下发现e的赋值跟r有关,而r是传入的参数

然后走一遍看e是如何生成的,接着根据调用堆栈一路往上追到了这里,发现到头了;

f的值是o[“value”],而ocnext下一个,然后就找不到这个数组从哪来的了,但多次调试发现他是预设值,所以大概是初始化的时候就已经生成了;然后我某次刷新的时候发现断在这里的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,而ofKr下面两个VrWr的值,稍微还原一下第一个参数值,就能发现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是之前分析过了,zonIddtgetconf的返回值中和,irTokenup返回值,所以来看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结束再看cbcb也是一抹多混淆,但在我解的过程中越看越熟悉,这不就是之前的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


dtid已知, tokenget返回值,cb后面查看发现调用的同一个函数,callback最开始分析过了,主要就是data中的几个参数,然后这里多了个extraData的参数,那就直接搜这个,直接就定位到了这里,

查看堆栈找到了data的几个参数,

参数源头都在上面,需要一层一层的分析函数,比较繁琐,经分析得知,
m为空,dpfext最外层加密一致,他们参数不一样,
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))))


ptoken和滑块X轴偏移的像素,需要通过图片识别得到,图片识别就直接跳过了,本来想训练个大模型识别,奈何自身的技能点数不够,所以后面直接获取图片人工测试;

[Python] 纯文本查看 复制代码
token, str(X_end / 320 * 100)


exttoken鼠标按下次数”+“,”+”轨迹列表长度的拼接,套个加密;好了参数全了,最后测试一下,完美!


====================================================================================================

###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,然后核心还是同一个加密函数,但凡多搞几种都没有心情分析了,整体连贯起来尝试,


登录成功!以上就是全过程了!

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-5 05:00

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表