frist
This commit is contained in:
11
reference/__init__.py
Normal file
11
reference/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
"""
|
||||
Reference 模块 - Sentinel 解决方案
|
||||
|
||||
包含 Sentinel Token 生成的完整实现
|
||||
"""
|
||||
|
||||
from .sentinel_solver import SentinelSolver
|
||||
from .js_executor import JSExecutor
|
||||
from .pow_solver import ProofOfWorkSolver
|
||||
|
||||
__all__ = ["SentinelSolver", "JSExecutor", "ProofOfWorkSolver"]
|
||||
14
reference/config.py
Normal file
14
reference/config.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
Reference 模块配置文件
|
||||
|
||||
供 Sentinel 解决器使用的配置项
|
||||
"""
|
||||
|
||||
# 调试模式
|
||||
DEBUG = False
|
||||
|
||||
# SDK JS 文件路径
|
||||
SDK_JS_PATH = "/home/carry/myprj/gptAutoPlus/sdk/sdk.js"
|
||||
|
||||
# 导出
|
||||
__all__ = ["DEBUG", "SDK_JS_PATH"]
|
||||
167
reference/fingerprint.py
Normal file
167
reference/fingerprint.py
Normal file
@@ -0,0 +1,167 @@
|
||||
# modules/fingerprint.py
|
||||
"""浏览器指纹生成器"""
|
||||
|
||||
import uuid
|
||||
import random
|
||||
import time
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Any
|
||||
|
||||
from config import FINGERPRINT_CONFIG
|
||||
|
||||
|
||||
class BrowserFingerprint:
|
||||
"""生成符合 SDK 期望的浏览器指纹"""
|
||||
|
||||
def __init__(self, session_id: str = None):
|
||||
self.session_id = session_id or str(uuid.uuid4())
|
||||
|
||||
# 新增: 使用确定性方法从 session_id 派生 Stripe 指纹
|
||||
import hashlib
|
||||
seed = hashlib.sha256(self.session_id.encode()).hexdigest()
|
||||
# seed 是64个hex字符,我们需要确保切片正确
|
||||
|
||||
# 从 seed 生成一致的 guid/muid/sid
|
||||
# UUID需要32个hex字符(去掉连字符),额外部分直接拼接
|
||||
self.stripe_guid = seed[:8] + '-' + seed[8:12] + '-' + seed[12:16] + '-' + seed[16:20] + '-' + seed[20:32] + seed[32:40]
|
||||
self.stripe_muid = seed[:8] + '-' + seed[8:12] + '-' + seed[12:16] + '-' + seed[16:20] + '-' + seed[20:32] + seed[40:46]
|
||||
self.stripe_sid = seed[:8] + '-' + seed[8:12] + '-' + seed[12:16] + '-' + seed[16:20] + '-' + seed[20:32] + seed[46:52]
|
||||
|
||||
self.user_agent = FINGERPRINT_CONFIG['user_agent']
|
||||
self.screen_width = FINGERPRINT_CONFIG['screen_width']
|
||||
self.screen_height = FINGERPRINT_CONFIG['screen_height']
|
||||
self.languages = FINGERPRINT_CONFIG['languages']
|
||||
self.hardware_concurrency = FINGERPRINT_CONFIG['hardware_concurrency']
|
||||
|
||||
def get_config_array(self) -> List[Any]:
|
||||
"""
|
||||
生成 SDK getConfig() 函数返回的 18 元素数组
|
||||
|
||||
对应 SDK 源码:
|
||||
[0]: screen.width + screen.height
|
||||
[1]: new Date().toString()
|
||||
[2]: performance.memory.jsHeapSizeLimit (可选)
|
||||
[3]: nonce (PoW 填充)
|
||||
[4]: navigator.userAgent
|
||||
[5]: 随机 script.src
|
||||
[6]: build ID
|
||||
[7]: navigator.language
|
||||
[8]: navigator.languages.join(',')
|
||||
[9]: 运行时间 (PoW 填充)
|
||||
[10]: 随机 navigator 属性
|
||||
[11]: 随机 document key
|
||||
[12]: 随机 window key
|
||||
[13]: performance.now()
|
||||
[14]: session UUID
|
||||
[15]: URL search params
|
||||
[16]: navigator.hardwareConcurrency
|
||||
[17]: performance.timeOrigin
|
||||
"""
|
||||
|
||||
# 模拟的 script sources
|
||||
fake_scripts = [
|
||||
"https://sentinel.openai.com/sentinel/97790f37/sdk.js",
|
||||
"https://chatgpt.com/static/js/main.abc123.js",
|
||||
"https://cdn.oaistatic.com/_next/static/chunks/main.js",
|
||||
]
|
||||
|
||||
# 模拟的 navigator 属性名
|
||||
navigator_props = [
|
||||
'hardwareConcurrency', 'language', 'languages',
|
||||
'platform', 'userAgent', 'vendor'
|
||||
]
|
||||
|
||||
# 模拟的 document keys
|
||||
document_keys = ['body', 'head', 'documentElement', 'scripts']
|
||||
|
||||
# 模拟的 window keys
|
||||
window_keys = ['performance', 'navigator', 'document', 'location']
|
||||
|
||||
current_time = time.time() * 1000
|
||||
|
||||
return [
|
||||
self.screen_width + self.screen_height, # [0]
|
||||
str(datetime.now()), # [1]
|
||||
None, # [2] memory
|
||||
None, # [3] nonce (placeholder)
|
||||
self.user_agent, # [4]
|
||||
random.choice(fake_scripts), # [5]
|
||||
"97790f37", # [6] build ID
|
||||
self.languages[0], # [7]
|
||||
",".join(self.languages), # [8]
|
||||
None, # [9] runtime (placeholder)
|
||||
f"{random.choice(navigator_props)}−{random.randint(1, 16)}", # [10]
|
||||
random.choice(document_keys), # [11]
|
||||
random.choice(window_keys), # [12]
|
||||
current_time, # [13]
|
||||
self.session_id, # [14]
|
||||
"", # [15] URL params
|
||||
self.hardware_concurrency, # [16]
|
||||
current_time - random.uniform(100, 1000), # [17] timeOrigin
|
||||
]
|
||||
|
||||
def get_cookies(self) -> Dict[str, str]:
|
||||
"""生成初始 cookies"""
|
||||
return {
|
||||
'oai-did': self.session_id,
|
||||
}
|
||||
|
||||
def get_stripe_fingerprint(self) -> Dict[str, str]:
|
||||
"""获取 Stripe 支付指纹(与 session_id 一致派生)"""
|
||||
return {
|
||||
'guid': self.stripe_guid,
|
||||
'muid': self.stripe_muid,
|
||||
'sid': self.stripe_sid,
|
||||
}
|
||||
|
||||
def get_headers(self, with_sentinel: str = None, host: str = 'auth.openai.com') -> Dict[str, str]:
|
||||
"""生成 HTTP headers(支持多域名)"""
|
||||
|
||||
# 基础 headers
|
||||
headers = {
|
||||
'User-Agent': self.user_agent,
|
||||
'Accept': 'application/json',
|
||||
'Accept-Language': f"{self.languages[0]},{self.languages[1]};q=0.5",
|
||||
# Note: urllib3/requests only auto-decompress brotli/zstd when optional
|
||||
# deps are installed; avoid advertising unsupported encodings.
|
||||
'Accept-Encoding': 'gzip, deflate',
|
||||
'Connection': 'keep-alive',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-origin',
|
||||
'Priority': 'u=1, i',
|
||||
'Pragma': 'no-cache',
|
||||
'Cache-Control': 'no-cache',
|
||||
}
|
||||
|
||||
# 根据域名设置特定 headers
|
||||
if 'chatgpt.com' in host:
|
||||
headers.update({
|
||||
'Origin': 'https://chatgpt.com',
|
||||
'Referer': 'https://chatgpt.com/',
|
||||
})
|
||||
else:
|
||||
headers.update({
|
||||
'Origin': 'https://auth.openai.com',
|
||||
'Referer': 'https://auth.openai.com/create-account/password',
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
# Sentinel token
|
||||
if with_sentinel:
|
||||
headers['openai-sentinel-token'] = with_sentinel
|
||||
|
||||
# Datadog RUM tracing
|
||||
trace_id = random.randint(10**18, 10**19 - 1)
|
||||
parent_id = random.randint(10**18, 10**19 - 1)
|
||||
|
||||
headers.update({
|
||||
'traceparent': f'00-0000000000000000{trace_id:016x}-{parent_id:016x}-01',
|
||||
'tracestate': 'dd=s:1;o:rum',
|
||||
'x-datadog-origin': 'rum',
|
||||
'x-datadog-parent-id': str(parent_id),
|
||||
'x-datadog-sampling-priority': '1',
|
||||
'x-datadog-trace-id': str(trace_id),
|
||||
})
|
||||
|
||||
return headers
|
||||
245
reference/js_executor.py
Normal file
245
reference/js_executor.py
Normal file
@@ -0,0 +1,245 @@
|
||||
"""JavaScript 执行引擎封装
|
||||
|
||||
说明:
|
||||
- OpenAI 的 `assets/sdk.js` 是浏览器脚本,包含多段 anti-debug 代码与 DOM 初始化逻辑。
|
||||
- 直接用 PyExecJS/ExecJS 在 Node 环境执行时,常见表现是「compile 成功但 call 卡死」。
|
||||
- 这里改为通过 `node -` 运行一段自包含脚本:先做轻量净化 + browser shim,再执行目标函数并强制输出结果。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from reference.config import DEBUG, SDK_JS_PATH
|
||||
|
||||
|
||||
class JSExecutor:
|
||||
"""通过 Node.js 执行 Sentinel SDK 内部逻辑(支持 async Turnstile VM)"""
|
||||
|
||||
def __init__(self):
|
||||
self._sdk_code: str = ""
|
||||
self._load_sdk()
|
||||
|
||||
def _load_sdk(self) -> None:
|
||||
sdk_path = Path(SDK_JS_PATH)
|
||||
if not sdk_path.exists():
|
||||
raise FileNotFoundError(f"SDK not found at {SDK_JS_PATH}")
|
||||
|
||||
sdk_code = sdk_path.read_text(encoding="utf-8")
|
||||
sdk_code = self._sanitize_sdk(sdk_code)
|
||||
sdk_code = self._inject_internal_exports(sdk_code)
|
||||
|
||||
self._sdk_code = sdk_code
|
||||
|
||||
if DEBUG:
|
||||
print("[JSExecutor] SDK loaded successfully (sanitized)")
|
||||
|
||||
def _sanitize_sdk(self, sdk_code: str) -> str:
|
||||
"""移除会在 Node 环境中导致卡死/超慢的 anti-debug 片段。"""
|
||||
# 1) 删除少量已知的顶层 anti-debug 直接调用(独占一行)
|
||||
sdk_code = re.sub(r"(?m)^\s*[rugkU]\(\);\s*$", "", sdk_code)
|
||||
sdk_code = re.sub(r"(?m)^\s*o\(\);\s*$", "", sdk_code)
|
||||
|
||||
# 2) 删除 `Pt(),` 这种逗号表达式里的 anti-debug 调用(避免语法破坏)
|
||||
sdk_code = re.sub(r"\bPt\(\),\s*", "", sdk_code)
|
||||
sdk_code = re.sub(r"\bPt\(\);\s*", "", sdk_code)
|
||||
|
||||
# 3) 删除 class 字段初始化里的 anti-debug 调用:`return n(), "" + Math.random();`
|
||||
sdk_code = re.sub(
|
||||
r'return\s+n\(\),\s*""\s*\+\s*Math\.random\(\)\s*;',
|
||||
'return "" + Math.random();',
|
||||
sdk_code,
|
||||
)
|
||||
|
||||
# 4) 删除类似 `if ((e(), cond))` 的逗号 anti-debug 调用(保留 cond)
|
||||
# 仅处理极短标识符,避免误伤正常逻辑;保留 Turnstile VM 的 `vt()`。
|
||||
def _strip_comma_call(match: re.Match[str]) -> str:
|
||||
fn = match.group(1)
|
||||
if fn == "vt":
|
||||
return match.group(0)
|
||||
return "("
|
||||
|
||||
sdk_code = re.sub(
|
||||
r"\(\s*([A-Za-z_$][A-Za-z0-9_$]{0,2})\(\)\s*,",
|
||||
_strip_comma_call,
|
||||
sdk_code,
|
||||
)
|
||||
return sdk_code
|
||||
|
||||
def _inject_internal_exports(self, sdk_code: str) -> str:
|
||||
"""把 SDK 内部对象导出到 `SentinelSDK` 上,便于在外部调用。"""
|
||||
# SDK 末尾一般是:
|
||||
# (t.init = un),
|
||||
# (t.token = an),
|
||||
# t
|
||||
# );
|
||||
pattern = re.compile(
|
||||
r"\(\s*t\.init\s*=\s*un\s*\)\s*,\s*\(\s*t\.token\s*=\s*an\s*\)\s*,\s*t\s*\)",
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
replacement = (
|
||||
"(t.init = un),"
|
||||
"(t.token = an),"
|
||||
"(t.__O = O),"
|
||||
"(t.__P = P),"
|
||||
"(t.__bt = bt),"
|
||||
"(t.__kt = kt),"
|
||||
"(t.__Kt = Kt),"
|
||||
"t)"
|
||||
)
|
||||
|
||||
new_code, n = pattern.subn(replacement, sdk_code, count=1)
|
||||
if n != 1:
|
||||
raise RuntimeError("Failed to patch SDK exports; SDK format may have changed.")
|
||||
return new_code
|
||||
|
||||
def _node_script(self, payload: Dict[str, Any], entry: str) -> str:
|
||||
payload_json = json.dumps(payload, ensure_ascii=False)
|
||||
|
||||
shim = r"""
|
||||
// --- minimal browser shims for Node ---
|
||||
if (typeof globalThis.window !== "object") globalThis.window = globalThis;
|
||||
if (!window.top) window.top = window;
|
||||
if (!window.location) window.location = { href: "https://auth.openai.com/create-account/password", search: "", pathname: "/create-account/password", origin: "https://auth.openai.com" };
|
||||
if (!window.addEventListener) window.addEventListener = function(){};
|
||||
if (!window.removeEventListener) window.removeEventListener = function(){};
|
||||
if (!window.postMessage) window.postMessage = function(){};
|
||||
if (!window.__sentinel_token_pending) window.__sentinel_token_pending = [];
|
||||
if (!window.__sentinel_init_pending) window.__sentinel_init_pending = [];
|
||||
|
||||
if (typeof globalThis.document !== "object") globalThis.document = {};
|
||||
if (!document.scripts) document.scripts = [];
|
||||
if (!document.cookie) document.cookie = "";
|
||||
if (!document.documentElement) document.documentElement = { getAttribute: () => null };
|
||||
if (!document.currentScript) document.currentScript = null;
|
||||
if (!document.body) document.body = { appendChild: function(){}, getAttribute: () => null };
|
||||
if (!document.createElement) document.createElement = function(tag){
|
||||
return {
|
||||
tagName: String(tag||"").toUpperCase(),
|
||||
style: {},
|
||||
setAttribute: function(){},
|
||||
getAttribute: function(){ return null; },
|
||||
addEventListener: function(){},
|
||||
removeEventListener: function(){},
|
||||
src: "",
|
||||
contentWindow: { postMessage: function(){}, addEventListener: function(){}, removeEventListener: function(){} },
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof globalThis.navigator !== "object") globalThis.navigator = { userAgent: "ua", language: "en-US", languages: ["en-US","en"], hardwareConcurrency: 8 };
|
||||
if (typeof globalThis.screen !== "object") globalThis.screen = { width: 1920, height: 1080 };
|
||||
if (typeof globalThis.btoa !== "function") globalThis.btoa = (str) => Buffer.from(str, "binary").toString("base64");
|
||||
if (typeof globalThis.atob !== "function") globalThis.atob = (b64) => Buffer.from(b64, "base64").toString("binary");
|
||||
window.btoa = globalThis.btoa;
|
||||
window.atob = globalThis.atob;
|
||||
"""
|
||||
|
||||
wrapper = f"""
|
||||
const __payload = {payload_json};
|
||||
|
||||
function __makeSolver(configArray) {{
|
||||
const solver = new SentinelSDK.__O();
|
||||
solver.sid = configArray?.[14];
|
||||
// 强制使用 Python 传入的 configArray,避免依赖真实浏览器对象
|
||||
solver.getConfig = () => configArray;
|
||||
return solver;
|
||||
}}
|
||||
|
||||
async function __entry() {{
|
||||
{entry}
|
||||
}}
|
||||
|
||||
(async () => {{
|
||||
try {{
|
||||
const result = await __entry();
|
||||
process.stdout.write(JSON.stringify({{ ok: true, result }}), () => process.exit(0));
|
||||
}} catch (err) {{
|
||||
const msg = (err && (err.stack || err.message)) ? (err.stack || err.message) : String(err);
|
||||
process.stdout.write(JSON.stringify({{ ok: false, error: msg }}), () => process.exit(1));
|
||||
}}
|
||||
}})();
|
||||
"""
|
||||
return "\n".join([shim, self._sdk_code, wrapper])
|
||||
|
||||
def _run_node(self, payload: Dict[str, Any], entry: str, timeout_s: int = 30) -> Any:
|
||||
script = self._node_script(payload, entry)
|
||||
|
||||
if DEBUG:
|
||||
print("[JSExecutor] Running Node worker...")
|
||||
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
["node", "-"],
|
||||
input=script,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=timeout_s,
|
||||
)
|
||||
except FileNotFoundError as e:
|
||||
raise RuntimeError("Node.js not found on PATH (required for Sentinel SDK execution).") from e
|
||||
except subprocess.TimeoutExpired as e:
|
||||
raise TimeoutError(f"Node worker timed out after {timeout_s}s") from e
|
||||
|
||||
stdout = (proc.stdout or "").strip()
|
||||
if not stdout:
|
||||
raise RuntimeError(f"Node worker produced no output (stderr={proc.stderr!r})")
|
||||
|
||||
try:
|
||||
obj = json.loads(stdout)
|
||||
except json.JSONDecodeError as e:
|
||||
raise RuntimeError(f"Node worker returned non-JSON output: {stdout[:200]!r}") from e
|
||||
|
||||
if not obj.get("ok"):
|
||||
raise RuntimeError(obj.get("error", "Unknown JS error"))
|
||||
|
||||
return obj.get("result")
|
||||
|
||||
def solve_pow(self, seed: str, difficulty: str, config_array: list) -> str:
|
||||
if DEBUG:
|
||||
print(f"[JSExecutor] Solving PoW: seed={seed[:10]}..., difficulty={difficulty}")
|
||||
|
||||
result = self._run_node(
|
||||
{"seed": seed, "difficulty": difficulty, "configArray": config_array},
|
||||
entry="return __makeSolver(__payload.configArray)._generateAnswerSync(__payload.seed, __payload.difficulty);",
|
||||
timeout_s=60,
|
||||
)
|
||||
|
||||
if DEBUG and isinstance(result, str):
|
||||
print(f"[JSExecutor] PoW solved: {result[:50]}...")
|
||||
|
||||
return result
|
||||
|
||||
def generate_requirements(self, seed: str, config_array: list) -> str:
|
||||
result = self._run_node(
|
||||
{"seed": seed, "configArray": config_array},
|
||||
entry=(
|
||||
"const solver = __makeSolver(__payload.configArray);\n"
|
||||
"solver.requirementsSeed = __payload.seed;\n"
|
||||
"return solver._generateRequirementsTokenAnswerBlocking();"
|
||||
),
|
||||
timeout_s=30,
|
||||
)
|
||||
return result
|
||||
|
||||
def execute_turnstile(self, dx_bytecode: str, xor_key: str) -> str:
|
||||
if DEBUG:
|
||||
print("[JSExecutor] Executing Turnstile VM...")
|
||||
|
||||
result = self._run_node(
|
||||
{"dx": dx_bytecode, "xorKey": xor_key},
|
||||
entry=(
|
||||
"SentinelSDK.__kt(__payload.xorKey);\n"
|
||||
"return await SentinelSDK.__bt(__payload.dx);"
|
||||
),
|
||||
timeout_s=30,
|
||||
)
|
||||
|
||||
if DEBUG and isinstance(result, str):
|
||||
print(f"[JSExecutor] Turnstile result: {result[:50]}...")
|
||||
|
||||
return result
|
||||
114
reference/pow_solver.py
Normal file
114
reference/pow_solver.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import time
|
||||
import json
|
||||
import base64
|
||||
from typing import List
|
||||
|
||||
DEBUG = True
|
||||
|
||||
class ProofOfWorkSolver:
|
||||
"""解决 OpenAI Sentinel 的 Proof of Work challenge"""
|
||||
|
||||
def __init__(self):
|
||||
# FNV-1a 常量
|
||||
self.FNV_OFFSET = 2166136261
|
||||
self.FNV_PRIME = 16777619
|
||||
|
||||
def fnv1a_hash(self, data: str) -> str:
|
||||
"""FNV-1a hash 算法"""
|
||||
hash_value = self.FNV_OFFSET
|
||||
|
||||
for char in data:
|
||||
hash_value ^= ord(char)
|
||||
hash_value = (hash_value * self.FNV_PRIME) & 0xFFFFFFFF
|
||||
|
||||
# 额外的混合步骤(从 JS 代码复制)
|
||||
hash_value ^= hash_value >> 16
|
||||
hash_value = (hash_value * 2246822507) & 0xFFFFFFFF
|
||||
hash_value ^= hash_value >> 13
|
||||
hash_value = (hash_value * 3266489909) & 0xFFFFFFFF
|
||||
hash_value ^= hash_value >> 16
|
||||
|
||||
# 转为 8 位十六进制字符串
|
||||
return format(hash_value, '08x')
|
||||
|
||||
def serialize_array(self, arr: List) -> str:
|
||||
"""模拟 JS 的 T() 函数:JSON.stringify + Base64"""
|
||||
json_str = json.dumps(arr, separators=(',', ':'))
|
||||
return base64.b64encode(json_str.encode()).decode()
|
||||
|
||||
def build_fingerprint_array(self, nonce: int, elapsed_ms: int) -> List:
|
||||
"""构建指纹数组(简化版)"""
|
||||
return [
|
||||
0, # [0] screen dimensions
|
||||
"", # [1] timestamp
|
||||
0, # [2] memory
|
||||
nonce, # [3] nonce ← 关键
|
||||
"", # [4] user agent
|
||||
"", # [5] random element
|
||||
"", # [6] script src
|
||||
"", # [7] language
|
||||
"", # [8] languages
|
||||
elapsed_ms, # [9] elapsed time ← 关键
|
||||
"", # [10] random function
|
||||
"", # [11] keys
|
||||
"", # [12] window keys
|
||||
0, # [13] performance.now()
|
||||
"", # [14] uuid
|
||||
"", # [15] URL params
|
||||
0, # [16] hardware concurrency
|
||||
0 # [17] timeOrigin
|
||||
]
|
||||
|
||||
def solve(self, seed: str, difficulty: str, max_iterations: int = 10000000) -> str:
|
||||
"""
|
||||
解决 PoW challenge
|
||||
|
||||
Args:
|
||||
seed: Challenge seed
|
||||
difficulty: 目标难度(十六进制字符串)
|
||||
max_iterations: 最大尝试次数
|
||||
|
||||
Returns:
|
||||
序列化的答案(包含 nonce)
|
||||
"""
|
||||
if DEBUG:
|
||||
print(f"[PoW] Solving challenge:")
|
||||
print(f" Seed: {seed}")
|
||||
print(f" Difficulty: {difficulty}")
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
for nonce in range(max_iterations):
|
||||
elapsed_ms = int((time.time() - start_time) * 1000)
|
||||
|
||||
# 构建指纹数组
|
||||
fingerprint = self.build_fingerprint_array(nonce, elapsed_ms)
|
||||
|
||||
# 序列化
|
||||
serialized = self.serialize_array(fingerprint)
|
||||
|
||||
# 计算 hash(seed + serialized)
|
||||
hash_input = seed + serialized
|
||||
hash_result = self.fnv1a_hash(hash_input)
|
||||
|
||||
# 检查是否满足难度要求
|
||||
# 比较方式:hash 的前 N 位(作为整数)<= difficulty(作为整数)
|
||||
difficulty_len = len(difficulty)
|
||||
hash_prefix = hash_result[:difficulty_len]
|
||||
|
||||
if hash_prefix <= difficulty:
|
||||
elapsed = time.time() - start_time
|
||||
if DEBUG:
|
||||
print(f"[PoW] ✓ Found solution in {elapsed:.2f}s")
|
||||
print(f" Nonce: {nonce}")
|
||||
print(f" Hash: {hash_result}")
|
||||
print(f" Serialized: {serialized[:100]}...")
|
||||
|
||||
# 返回 serialized + "~S" (表示成功)
|
||||
return serialized + "~S"
|
||||
|
||||
# 每 100k 次迭代打印进度
|
||||
if DEBUG and nonce > 0 and nonce % 100000 == 0:
|
||||
print(f"[PoW] Tried {nonce:,} iterations...")
|
||||
|
||||
raise Exception(f"Failed to solve PoW after {max_iterations:,} iterations")
|
||||
1129
reference/register.py
Normal file
1129
reference/register.py
Normal file
File diff suppressed because it is too large
Load Diff
113
reference/sentinel_solver.py
Normal file
113
reference/sentinel_solver.py
Normal file
@@ -0,0 +1,113 @@
|
||||
# modules/sentinel_solver.py
|
||||
"""Sentinel 挑战求解器"""
|
||||
|
||||
import json
|
||||
import uuid
|
||||
from typing import Dict, Optional
|
||||
|
||||
from reference.js_executor import JSExecutor
|
||||
from utils.fingerprint import BrowserFingerprint
|
||||
from reference.config import DEBUG
|
||||
|
||||
|
||||
class SentinelSolver:
|
||||
"""协调指纹生成和 JS 执行,生成完整的 Sentinel tokens"""
|
||||
|
||||
def __init__(self, fingerprint: BrowserFingerprint):
|
||||
self.fingerprint = fingerprint
|
||||
self.js_executor = JSExecutor()
|
||||
|
||||
def generate_requirements_token(self) -> Dict[str, str]:
|
||||
"""
|
||||
生成 requirements token(初始化时需要)
|
||||
|
||||
Returns:
|
||||
{'p': 'gAAAAAC...', 'id': 'uuid'}
|
||||
"""
|
||||
if DEBUG:
|
||||
print("[Solver] Generating requirements token...")
|
||||
|
||||
# 生成随机 seed
|
||||
req_seed = str(uuid.uuid4())
|
||||
|
||||
# 获取指纹配置
|
||||
config_array = self.fingerprint.get_config_array()
|
||||
|
||||
# 调用 JS 求解
|
||||
answer = self.js_executor.generate_requirements(req_seed, config_array)
|
||||
|
||||
token = {
|
||||
'p': f'gAAAAAC{answer}',
|
||||
'id': self.fingerprint.session_id,
|
||||
}
|
||||
|
||||
if DEBUG:
|
||||
print(f"[Solver] Requirements token: {token['p'][:30]}...")
|
||||
|
||||
return token
|
||||
|
||||
def solve_enforcement(self, enforcement_config: Dict) -> str:
|
||||
"""
|
||||
解决完整的 enforcement 挑战(PoW + Turnstile)
|
||||
|
||||
Args:
|
||||
enforcement_config: 服务器返回的挑战配置
|
||||
{
|
||||
'proofofwork': {
|
||||
'seed': '...',
|
||||
'difficulty': '0003a',
|
||||
'token': '...', # cached token
|
||||
'turnstile': {
|
||||
'dx': '...' # VM bytecode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Returns:
|
||||
完整的 Sentinel token (JSON string)
|
||||
"""
|
||||
if DEBUG:
|
||||
print("[Solver] Solving enforcement challenge...")
|
||||
|
||||
pow_data = enforcement_config.get('proofofwork', {})
|
||||
|
||||
# 1. 解决 PoW
|
||||
seed = pow_data['seed']
|
||||
difficulty = pow_data['difficulty']
|
||||
|
||||
config_array = self.fingerprint.get_config_array()
|
||||
pow_answer = self.js_executor.solve_pow(seed, difficulty, config_array)
|
||||
|
||||
# 2. 执行 Turnstile(如果有)
|
||||
turnstile_result = None
|
||||
turnstile_data = pow_data.get('turnstile')
|
||||
|
||||
if turnstile_data and turnstile_data.get('dx'):
|
||||
dx_bytecode = turnstile_data['dx']
|
||||
xor_key = self.fingerprint.session_id # 通常用 session ID 作为密钥
|
||||
|
||||
turnstile_result = self.js_executor.execute_turnstile(dx_bytecode, xor_key)
|
||||
|
||||
# 3. 构建最终 token
|
||||
sentinel_token = {
|
||||
# enforcement token 前缀为 gAAAAAB(requirements 为 gAAAAAC)
|
||||
'p': f'gAAAAAB{pow_answer}',
|
||||
'id': self.fingerprint.session_id,
|
||||
'flow': 'username_password_create',
|
||||
}
|
||||
|
||||
# 添加可选字段
|
||||
if turnstile_result:
|
||||
sentinel_token['t'] = turnstile_result
|
||||
|
||||
if pow_data.get('token'):
|
||||
sentinel_token['c'] = pow_data['token']
|
||||
|
||||
token_json = json.dumps(sentinel_token)
|
||||
|
||||
if DEBUG:
|
||||
print(f"[Solver] Sentinel token generated: {token_json[:80]}...")
|
||||
|
||||
return token_json
|
||||
|
||||
|
||||
Reference in New Issue
Block a user