@lawwaikit #11 能帮到忙就好,哈哈
用户 ID 692 · 当前名首次快照 2026-08-12 10:15:21 · 当前名始于 2026-08-12 10:15:21 · 原站主页: https://linux.sb/user/692
共 44 楼
@lawwaikit #11 能帮到忙就好,哈哈
@签到 #9 好的,等后面有时间更新一下
@神居于天 #13 那必须可以啊
明天签到积分就够了,提前申请一下😄
喜不喜欢neo的批量封号😆
@哈雷彗星 #10 禁止贴脸开大
可以的话还是搞个oauth,这样有什么服务可以绑定烧饼站
笑死了,我这个咋样
@痛失姓名的站长 #5 做大做强哈哈
@roiding #1 哈哈哈,这种程度的逆向都不需要skill
目标:看雪登录(passport.kanxue.com)的网易易盾滑块,纯 Node 全自动通过。
结果:/api/v3/check 返回 result:true,连续 5/5;最终 validate 提交看雪登录接口被接受。
版本信息(所有结论绑定此版本):
| 项 | 值 |
|---|---|
| 验证码 ID | e019d55cf18348519c201a0d2f80ebfb |
| 加载器 | load.min.js v2.5.4 |
| core | core-optimi.m25b40.v2.28.5.min.js(631KB) |
| 协议 | c.dun.163.com/api/v3/* |
登录页点出滑块,Network 里就三个接口:
| 接口 | 作用 | 关键参数 |
|---|---|---|
GET /api/v2/getconf | 初始化配置 | 返回 dt、resources、apiServer、staticServers |
GET /api/v3/get | 拉验证码 | fp、cb、dt、id…返回 token + 背景图/拼图 URL |
GET /api/v3/check | 提交结果 | token、data、cb…返回 result + validate |



referer=https://passport.kanxue.com/user-account_login.htm
zoneId=CN31 # getconf 下发
dt=... # getconf 下发
acToken= # 本 captchaId 为空
id=e019d55cf18348519c201a0d2f80ebfb
fp=... # 指纹,见 3.1
https=true
type= # 空 = 默认滑块
version=2.28.5
dpr=1
dev=3 # 设备类型
cb=... # 随机串加密,见 3.2
ipv6=false
runEnv=10
group= scene=
lang=zh-CN
sdkVersion=
loadVersion=2.5.4
iv=4 # IV_VERSION,core 常量
user=
width=320
audio=false
sizeType=10
smsVersion=v3
token= # 刷新时带上旧 token
callback=__JSONP_xxx_0
id, token, dt, zoneId, referer
data={"d":"...","m":"","p":"...","f":"...","ext":"..."} # 核心
width=320 type=2 version=2.28.5 cb=... bf=0 runEnv=10 iv=4 loadVersion=2.5.4data 四个密文字段(m 恒为空),全部由本地生成。
core 六百多 KB,字符串全部收进一张表,调用统一走 a0_0x1e60(0x..):
var a0_0x3d6e=['doIJY3r0CJ','bDgVsRFVwz8', ...共1545条];
function a0_0x1e60(_0x3d6ee5,_0x1e6084){
// 自定义 base64 字母表: kyclmxfigrhbswquanzevpjotdKYCLMXFIGRHBSWQUANZEVPJOTD0123456789+/=
// 解码: 自定义base64 -> %xx -> decodeURIComponent
}注意:不是标准 base64,字母表是自定义的。解码器自带 gtHdNm 方法,内部做了缓存。
更坑的是:解完这层,最底下的大模块还有第二层——自己的数组、自己的解码器,字母表是 2izvR3Ydkgw605lf(16 字符,映射到 hex)。

解混淆不硬啃,三步:
a0_0x1e60(0x..) 全部替换成明文get 的 fp 是浏览器指纹。全局搜 gdxidpyhxde 只找到一处读取,没有写入——因为生成它的模块把字符串名也编进了自己的嵌套表。
实际链路:
gdxidpyhxdEwindow.gdxidpyhxde 由 cookie 解码而来自定义base64密文:时间戳,如 VppCnhP7SV+Y\pbQCE8...:1786798405363
处理:把生成模块原样塞进 Node vm,补假的 window/document/navigator/localStorage/screen,跑完 window.gdxidpyhxde 就有值了,与浏览器一致。整个指纹生成器不用自己复刻。

cb 是每次请求都会变的随机串:
uuid = 32 位随机 [0-9A-Za-z]
在位置 [1, 10, 12, 13, 26, 31] 依次注入 "vfnv46" 六个字符
cb = aes(uuid)suffix、code、pos 三个常量(m25b40 / vfnv46 / 位置数组)与版本绑定,换版本要重取。
这个 aes 不是 AES,是易盾自己搓的分组密码。常量:
| 常量 | 值 |
|---|---|
| SEED_KEY | fd6a43ae25f74398b61c03c83be37449 |
| ROUND_KEY | 037606da0296055c |
| SBOX | 256 字节置换表(__SBOX__) |
| 私有 base64 字母表 | MB.CfHUzEeJpsuGkgNwhqiSaI4Fd9L6jYKZAxn1/Vml0c5rbXRP+8tD3QTO2vWyo |
| 填充符 | 7 |
加密流程:
块^密钥 → 加prev → 异或prev → S盒(S盒()),输出即下一轮 prev轮函数操作表(ROUND_KEY = 03 76 06 da 02 96 05 5c):
| 操作码 | 操作 |
|---|---|
| 0 | 不变 |
| 1 | 全块异或常量 |
| 2 | 全块加常量 |
| 3 | 逐字节异或递增 |
| 4 | 逐字节加递增 |
| 5 | 逐字节异或递减 |
| 6 | 逐字节加递减 |
逆运算(验证用):S 盒求逆 + 反向轮函数 + 反向链,CRC 校验通过即还原成功。后面解密真实样本全靠它。
dt:getconf 下发,存 localStorage["ujg3ps2znyw"]iv=4:core 常量 IV_VERSIONloadVersion=2.5.4:load.min.js 的 VERSIONd/m/p/f/ext 按模块逻辑生成后,get 正常(error:0),check 也正常返回 error:0,但 result 恒为 false。p 从 2 扫到 98(步长 2,同一 token 可重复 check)一个都不中。
此时能确定:不是位置问题,是数据本身有问题。但密文看不出问题在哪。
破局:在浏览器手动拖一次,抓通过的 check 请求(result:true),用自研 aes 逆运算把 data 全解密。拿到明文后,三个坑当场现形:
真实明文:"77.8125"。
前端代码是 parseInt(jigsaw.style.left) / width * 100 + '',直接字符串拼接,没有 round。我之前生成的是 Math.round(...),全灭。
f 的明文是 47 个浮点数,不是 dx,dy,t,trusted 坐标。前端先把轨迹丢进一个统计模块(module_0x38),算出 47 维特征再拼接:
轨迹点 -> [x序列, y序列, t序列]
-> 速度序列(vx, vy, 合速度) -> 加速度序列(ax, ay, a合)
-> 统计: 数量/去重数/均值/标准差/最小/最大/25分位/75分位真实样本 f 明文开头:
124,17,0.2042,4.776,142,0,0.8333,0.2133,0.1808,38,0.0625,0.3333,-0.3333,...直接发坐标 = 风控一眼假。
xorEncode(a, b) 输出长度由 b 决定:
xorEncode('数据', 'token') // 错:输出长度 = token 长度
xorEncode('token', '数据') // 对:输出长度 = 数据长度写反后密文长度直接不对,服务端自然不认。


经验:加密参数还原后,必须用真实通过样本对账。 猜出来的逻辑,错在哪个细节根本看不出来。
真实通过样本透露的轨迹参数:
ext = "2,142" # mouseDown 次数=2,轨迹点数=142
d = 50 个采样点 # SAMPLE_NUM=50,等间隔采样
点格式: "dx,dy,t,1" # 最后一位 trusted=1(真实鼠标)
最后一点: "260,3,1716,1" # 拖动 260px,耗时 1716ms对应关系:jigsawLeft=249 时,dragX=260,差值 10.5 来自 restrict() 映射(见第六节)。
生成逻辑(核心代码):
function genTrace(dragX, token) {
// 140 点 / 1750ms / ease-out / trusted=1
for (let i = 0; i < 140; i++) {
const t = Math.round(1750 * i / 139);
const dx = Math.max(0, Math.round(dragX * (1 - Math.pow(1 - i/139, 3))));
const dy = Math.round((Math.random() - 0.5) * 6);
atomTrace.push([dx, dy, t, 1]);
traceData.push(xorEncode(token, [dx, dy, t, 1].join(',')));
}
}
function buildData({ token, jigsawLeft, traceData, atomTrace }) {
const d = aes(sample(traceData, 50).join(':')); // 50 采样
const p = aes(xorEncode(token, String(jigsawLeft / 320 * 100))); // 不取整
const f = aes(xorEncode(token, features(uniqueByTime(atomTrace)).join(','))); // 47 维
const ext = aes(xorEncode(token, '2,' + traceData.length));
return JSON.stringify({ d, m: '', p, f, ext });
}check 通过后拿到服务端 validate,还要再算一次才是给业务方的最终值:
finalValidate = zoneId + '_' + md5(aes(validate + '::' + fp)) + '_v_i_1'
// 例: CN31_90fffa737f70291f7a08893899dc5d3c_v_i_1一开始用拼图小块对背景做模板匹配,匹配到一个无关角落。原因:背景的缺口是被抠空的,拿拼图去匹配空腔,必然失败。
正确做法:拼图 alpha 轮廓 vs 背景 Canny 边缘。
mask = alpha > 0 # 拼图不透明区域
dist = distanceTransform(mask) # 取轮廓(距边界 ≤2px)
outline = (dist <= 2) & (dist > 0)
edges = Canny(bg)
res = matchTemplate(edges, outline, TM_CCOEFF_NORMED)
# 取 y 在拼图 bbox 高度范围内的最优 x用人工通过的图验证:命中 x=251,真实 jigsawLeft=249,差 1px。

restrict() 映射细节:前端滑块有个 restrict(),拼图位移 ≠ 鼠标位移:
jigsawLeft = dragX - 10.5 # 常规区间
jigsawLeft = dragX / 2 # 拖前 21px 内所以想落到 jigsawLeft=249,鼠标要拖 259.5;轨迹终点的 dx 也必须用 dragX,不是 jigsawLeft。
验证:
check 连续 5/5 result:true/user-kanxue_login.htm,返回 帐号或密码错误(假账号下即验证码已过)

692
2年前的东西也搬
@嘿嘿喵喵 #3 只有win的。mac接口也不一样。没法通用。
@steve02081504 #2 这咋可能,没感光元件
@杀 #4 你用过外接显示器吗
路过的大哥大姐支持一下
昨天分享了一下酷安的签名纯算。今天分享一下用了很久的win系统亮度调节软件。
大家之前都应该用过Tray,但是一个亮度调节用electron写,有时候会吃上百mb内存,实在太恶心了,而且还会卡来卡去的。
干脆一气之下,自己写了一个,最基本的功能都有,调亮度,快捷键调亮度,用起来是够了。
不想在隔壁开源是因为发个github还要你加上承诺,那我宁愿不发。
————————————————————
Lumaris:轻量,丝滑,有特效
https://github.com/Elnyxn/Lumaris
感兴趣的可以试试看
————————————————————
希望大家多多支持!
@SBBot 锐评一下这个帖子
php的牛逼之处😃
人多了,顶个帖子。好东西不能埋没啊
下一个linuxsb.sb 站倒计时
真想让do站看看论坛也不是只有他一家的话,建议先思考思考怎么管理。你neo论坛起步的时候也没封过这么多人,反动之类的肯定要ban,我们也不搞事。现在这个苗头感觉发展下去,就真变小丑了。
@Adai #50 又加新功能了,还多了个ip地区显示
@StellaFortuna林可欣 #19 这种东西小范围享受就可以了
猫娘版本的看过了,有没有魅魔和雌小鬼版的可以看
恭喜,夏阁艾迪剑
让ai找点开源的屏蔽词库加到用户名里,然后已经注册的用户,让ai去数据库扫出来,官方账号之类的如果不允许昵称自动改成随机的,然后加入屏蔽词里。这样比人工禁言速度快多了。
公告提前说好哪些不允许。我咋还看见吕布也被封了。笑死了
@test1145 #3 6666,秽土转生
楼主喜欢
夏阁艾迪剑
在错误的地方
慢慢来吧,问题不大
@fucker #1 小南娘001
@夏天 #1 那必须不错
本站第一个逆向贴。
网易易盾的壳也是轻松dump。
啥时候支持md代码块啊
#!/usr/bin/env python3
"""Coolapk 16.x X-App-Token generator.
The algorithm was verified against libauth.so (version 16.5.1).
"""
import base64
import hashlib
import time
try:
import crypt
except ImportError as exc: # Python 3.13+ needs a libcrypt-compatible package.
raise RuntimeError("Python crypt module is required for bcrypt ($2y$)") from exc
PACKAGE_NAME = "com.coolapk.market"
VERSION_CODE = 2607271
_AUTH_BLOB_B64 = (
"Dg4YDxUcCykLah82Fg8XMQ0eHzALCR8pDyMfNxYeFBsPI2seChwDLxMjajMXDxdvEyAYDxAcOS4XIzEyFw8LaxAJax4VCQ8iFyAy"
"CxYMFy0MamopEyMyCRcOCykNDhweCBwfLQtqCAwXCQsxDwlqMBEJFy4QHjIDFgwLMQ8wHyARCQspF2oYDBYOCy0PM2pqFAkXKRdo"
"GAIWDBc2EBlrHhQcNhQWGQtqDQ4fIBYJCyILah8yFh8XNhMwHyARHDktFyBqNhYzFB8MIB8wCgkDLxQeMTEWHBctDDMiDhEcMS4L"
"IDICFhwXKQ0ZbjAWCQMuD2gYDg4OG2oWHB8vFyAyDhYwFzITMBweFxwXKRNqGAwWGQsxDxktIBAJEykTIBgJFx8XYg8ZIh8WCRMu"
"E2gYCxYPCzUPI2owFhwXKRMjDzEXHAstDDFqLggZIgMXDhc2EBluahQJHy0PaggLFhwXMhMzajEUCRctD2gYCxYwFD0PIy0wFRwfKRQZ"
"Ig4WHxctDRkiDxYJHy4TIwgOFh4UGBMjamoQHAAUFw8XKQ8eGB8QHD0uFBlqMRYeCzENGWseFxwXIggZMTAWDBdqDDMiDhcc"
"PSITI2o3FgwXIhAJaiAQHAsiF2gYCxYJC24NDhgeFxwXLxNqDzIOCS0gChw9LggeGAgXHhQfDwlqMBUcAy8QHjE2FwkLag8gGyAU"
"CRctCB4xMRcOFzEMCSIPFxwLLggZIgMXDhQcEyMiHxYcAykTIA8yFjMLagxqaiIUGSIMFhkXagwjbiAQHA8iFBkiDhYcC24MI24w"
"AxwDIhAeDzAWDxctDyAcHgocEykXah82Fh4XKQweG2oVCRcvFBkxNxYfCzEMMyIfFwkQFBYOFzUPMy0gFhw5Lg9qCAsWMwstDA4c"
"HhAJDy0PIzEwFw8XMhMjaiATCQ8pE2ofMBcOFy0PCWsPFBw5IggeHzYWDwsuEAluMRQcEykTIDIADg4YDgMcHy0LIwgDFgkXNhMJ"
"bmoWHD0pDB4IABYPCzUNDhgeCxwDLggZIg0WMBQfDB4YHhccMS4UHg82FhwLbgwJaw8QHD0tFyAIDRYeC24PD2otCyNqNxYPC24N"
"CWsOFwkXKQ8jDzMXDxQbDwkiDxYcAy8TaggPFjMXKQ0eHB8WHBciE2gYCxYMF2oNCW4xFRw5LRdqCAIWDBc1DRlqMBEcHyIWGRdq"
"DDMiDhQcGy4IHggNFg8LNhAZaw4QHBstFBkiPQMb"
)
def _md5(data: bytes) -> str:
return hashlib.md5(data).hexdigest()
def build_device_id(metadata: str, manufacturer: str, brand: str, model: str,
display: str, oaid: str) -> str:
"""Reproduce C9682.m64375()'s X-App-Device value."""
raw = f"{metadata}; ; ; ; {manufacturer}; {brand}; {model}; {display}; {oaid}"
encoded = base64.b64encode(raw.encode("utf-8")).decode("ascii")
return encoded[::-1].replace("=", "").replace("\r", "").replace("\n", "")
def _auth_stream() -> bytes:
encrypted = base64.b64decode(_AUTH_BLOB_B64)
return bytes(value ^ 0x5A for value in encrypted)
def get_token(device_id: str, timestamp: int | None = None,
version_code: int = VERSION_CODE,
package_name: str = PACKAGE_NAME) -> str:
"""Generate X-App-Token for the supplied X-App-Device value."""
if timestamp is None:
timestamp = int(time.time())
# libauth selects a 128-character base64 window using current time and version.
stream = _auth_stream()
index = (timestamp + version_code) % 100
start = 0x80 + index * 4
fragment = base64.b64decode(stream[start:start + 0x80])
payload = b"&".join((
package_name.encode("utf-8"),
fragment,
_md5(device_id.encode("utf-8")).encode("ascii"),
str(timestamp).encode("ascii"),
str(version_code).encode("ascii"),
))
encoded_payload = base64.b64encode(payload)
password = _md5(encoded_payload).encode("ascii")
payload_digest = _md5(payload).encode("ascii")
# Native code passes the complete base64 string to bcrypt; crypt uses its
# first 22 characters as the Blowfish salt.
salt = base64.b64encode(f"{timestamp:x}/".encode("ascii") + payload_digest).decode("ascii")
setting = "$2y$04$" + salt[:22]
bcrypt_result = crypt.crypt(password.decode("ascii"), setting)
if not bcrypt_result or bcrypt_result.startswith("*"):
raise RuntimeError("system crypt does not support bcrypt $2y$")
return "v3" + base64.b64encode(bcrypt_result.encode("ascii")).decode("ascii")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("device_id")
parser.add_argument("--timestamp", type=int)
args = parser.parse_args()
print(get_token(args.device_id, args.timestamp))
单字极品id
诞生才几个小时
啥时候接入一下快捷登录