App爬虫实战:突破SSL Pinning、动态签名与设备指纹的五层反爬

news2026/5/24 18:23:54
1. 这不是写个 requests 就能跑通的“爬虫”而是一场持续数月的攻防拉锯战“App 父亲”这个词在移动互联网圈里没人真叫但所有做过 App 数据采集的人心里都清楚——你面对的从来不是一串 API 接口而是一个被精心加固、层层设防、会主动识别、会动态变异、甚至会反向追踪的完整客户端系统。我第一次接手这个“App 爬虫实现案例对抗反爬虫机制”项目时客户只甩来一句话“iOS 和 Android 的首页商品列表每天凌晨同步一次要稳定跑半年以上。”听起来像基础活结果前三周我连登录态都没稳住。这不是传统网页爬虫的逻辑复刻。App 端的反爬早已脱离了 User-Agent 检查、Referer 验证这种初级阶段。它融合了设备指纹固化、SSL Pinning 强制校验、请求体 AESRSA 混合加密、时间戳/随机数/签名三元动态绑定、行为埋点反模拟、以及服务端实时风控模型拦截——五层嵌套环环相扣。你发一个包后端可能同时验证这台设备是不是真实手机证书链是否被篡改签名算法用的是哪一版密钥时间戳偏差是否超过 300ms上一个请求的滑动轨迹是否符合人类操作节奏漏掉任意一环返回的就不是数据而是 {code:403,msg:illegal request} 或者更隐蔽的 {code:200,data:[]}。关键词“App 爬虫”“反爬虫机制”“动态加密”“设备指纹”“SSL Pinning”不是标签是五道必须逐个击破的关卡。这篇文章不讲“如何用 Python 写个爬虫”而是还原我们团队在真实商业项目中从逆向分析、协议还原、环境模拟到长期运维的完整技术路径。适合两类人一是正被某款 App 卡在登录页、签名失败、频繁封号的工程师你需要的不是工具推荐而是可复现的破局思路二是技术负责人或架构师你想知道这类需求落地的真实成本、风险边界与可持续性设计。全文无黑产话术、无越狱/Root 教程、不教绕过法律合规红线只谈在合法授权、白盒可控前提下如何让自动化数据采集真正“活下来”。2. 为什么不能直接抓包——SSL Pinning 与证书透明度的双重绞杀2.1 抓包失败的第一道墙SSL Pinning 不是“开关”而是“熔断器”绝大多数新手遇到的第一个死结就是 Charles/Fiddler 抓不到任何有效请求。不是代理没配对不是证书没装上而是 App 在代码里硬编码了服务器公钥哈希值Certificate Pinning一旦发现当前 TLS 握手使用的证书与预置哈希不匹配立即终止连接——连 HTTP 请求头都发不出去。这不是 Bug是设计。我们拿到的这款电商 App在 Android 端使用 OkHttp 的 CertificatePinner核心代码片段如下CertificatePinner pinner new CertificatePinner.Builder() .add(api.example.com, sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) .add(api.example.com, sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB) .build(); OkHttpClient client new OkHttpClient.Builder() .certificatePinner(pinner) .build();注意这里写了两条哈希对应主备证书轮换策略。这意味着即使你成功替换了中间人证书只要哈希不匹配OkHttp 会在connect()阶段直接抛出javax.net.ssl.SSLPeerUnverifiedException根本不会走到后续的Request构建环节。提示别急着去 HookCertificatePinner.check()。很多 App 已将校验逻辑下沉到 NDK 层Java 层 Hook 后Native 层仍会二次校验。我们实测过仅 Hook Java 层成功率不足 30%。2.2 真实世界的证书校验不止 SHA256还有 SubjectPublicKeyInfo 全字段比对更棘手的是部分 App 并未使用标准的CertificatePinner而是自己实现了X509TrustManager并调用X509Certificate.getPublicKey().getEncoded()获取 DER 编码后的公钥字节再做 SHA256 哈希比对。这种写法导致你无法通过简单替换证书解决——因为中间人证书的公钥和原站完全不同哈希值天然不等。我们曾用 Frida 注入以下脚本定位校验点Java.perform(function () { var X509TrustManager Java.use(javax.net.ssl.X509TrustManager); X509TrustManager.checkServerTrusted.implementation function (chain, authType) { console.log([] checkServerTrusted called); console.log([-] Chain length: chain.length); if (chain.length 0) { var cert chain[0]; var pubKey cert.getPublicKey().getEncoded(); console.log([-] PubKey len: pubKey.length); // 打印前 32 字节用于比对 console.log([-] PubKey hex: bytesToHex(pubKey.slice(0, 32))); } return this.checkServerTrusted.call(this, chain, authType); }; });运行后发现App 实际比对的是pubKey的完整 DER 编码1172 字节而非仅公钥模值。这意味着你必须生成一个与原站完全相同公钥的证书——这在数学上不可行。唯一可行路径是让 App “相信”它正在跟原站通信即绕过校验逻辑本身而非伪造证书。2.3 可持续方案基于 Frida 的动态绕过而非静态 Patch静态 Patch APK如用 apktool 修改 smali看似一劳永逸但在实际运维中问题极大每次 App 更新smali 结构变动Patch 脚本全部失效Google Play Protect 会扫描修改过的 APK触发安装拦截多设备批量部署时需为每台设备重签、重装运维成本爆炸。我们最终采用Frida 注入 动态内存补丁方案核心逻辑是在checkServerTrusted方法执行前将其返回值强制设为void跳过所有校验。关键代码如下// frida-script.js Java.perform(function () { var TrustManagerImpl Java.use(com.android.org.conscrypt.TrustManagerImpl); TrustManagerImpl.checkServerTrusted.overload( [Ljava.security.cert.X509Certificate;, java.lang.String, java.lang.String ).implementation function (chain, authType, host) { console.log([*] Bypassing TrustManagerImpl.checkServerTrusted for host); return; // 直接返回不执行原逻辑 }; // 同时覆盖 OkHttp 的 CertificatePinner var CertificatePinner Java.use(okhttp3.CertificatePinner); CertificatePinner.check.overload(java.lang.String, [Ljava.security.cert.Certificate;).implementation function (hostname, peerCertificates) { console.log([*] Bypassing CertificatePinner.check for hostname); return; // 不抛异常放行 }; });该方案优势在于✅ 无需修改 APK 文件兼容任意版本更新✅ Frida Server 可静默后台运行用户无感知✅ 支持远程下发脚本灰度控制绕过范围如仅对 api.example.com 生效✅ 日志可回传便于监控绕过成功率我们加了埋点当checkServerTrusted被调用但未抛异常时记为“绕过成功”。注意Frida 需 root 权限但我们并未要求用户 Root 手机。方案是——将 Frida Server 预置在定制 ROM 中由设备厂商合作提供。这是商业项目中真正可行的“合规 root”路径既满足技术需求又规避终端用户侧风险。3. 签名算法还原从混淆的 Native 库到可复现的 Python 实现3.1 为什么抓到的请求Python 重放却一直 signature_invalid当你终于绕过 SSL Pinning抓到一条看似完整的请求POST /v2/product/list HTTP/1.1 Host: api.example.com Content-Type: application/json; charsetUTF-8 X-Signature: 8a7f3b2c1d9e4f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a X-Timestamp: 1715234567890 X-Nonce: a1b2c3d4e5f67890 {category_id:1001,page:1,size:20}把这段 Body、Header 复制进 Python用requests.post()重放99% 概率返回{code:401,msg:signature invalid}。原因很简单X-Signature不是固定字符串而是由Body Timestamp Nonce SecretKey 特定排序规则经多层加密生成的动态令牌。而 SecretKey 并不存于配置文件它藏在 Native so 库里。我们用readelf -d libcrypto.so | grep NEEDED查看依赖发现该 App 加载了自研的libsec.so大小仅 128KB但符号表被 strip 得极干净$ nm -D libsec.so | head -10 U __cxa_atexit U __cxa_finalize U __stack_chk_fail U __xstat U abort U calloc U clock_gettime U free U malloc U memcpy没有一个业务函数名。此时静态分析效率极低。我们转向动态调用追踪用 Frida Hook 所有dlopen和dlsym捕获libsec.so加载后Java 层调用的首个 JNI 函数名。Interceptor.attach(Module.findExportByName(libsec.so, Java_com_example_sec_SecHelper_sign), { onEnter: function (args) { console.log([] Java_com_example_sec_SecHelper_sign called); console.log([-] arg0 (jobject): args[0]); console.log([-] arg1 (jstring data): Java.vm.getEnv().getStringUtfChars(args[1], null)); console.log([-] arg2 (jstring ts): Java.vm.getEnv().getStringUtfChars(args[2], null)); console.log([-] arg3 (jstring nonce): Java.vm.getEnv().getStringUtfChars(args[3], null)); }, onLeave: function (retval) { console.log([-] sign result: retval); } });运行后清晰捕获到签名输入三元组dataJSON Body 字符串、ts毫秒时间戳、nonce16位随机小写字母数字。返回值是 64 位十六进制字符串——正是X-Signature的值。3.2 逆向libsec.so从 ARM64 汇编到 AES-CBC RSA-OAEP 的混合流程用 Ghidra 加载libsec.so定位Java_com_example_sec_SecHelper_sign函数。由于无符号我们通过字符串常量反推搜索AES/CBC/PKCS5Padding定位到关键函数sub_12340。反编译伪代码显示其核心逻辑// Step 1: 用固定 IV 和硬编码 AES Key 对 data ts nonce 拼接字符串进行 AES-CBC 加密 char *cipher_data aes_cbc_encrypt(data_str, 0123456789abcdef, 0000000000000000); // Step 2: 对 cipher_data 的二进制结果用内置 RSA 公钥长度 2048bit做 OAEP 填充后加密 unsigned char *rsa_encrypted rsa_oaep_encrypt(cipher_data, rsa_pubkey_der); // Step 3: 对 rsa_encrypted 结果做 Base64 编码并转小写 char *signature to_lower(base64_encode(rsa_encrypted));难点在于AES Key 和 RSA 公钥均以字节数组形式硬编码在.rodata段且被分段存储、异或混淆。我们用 Ghidra 的Data→Create Array功能结合 Frida 运行时 dump 内存最终还原出AES Key16字节0x7e 0x1a 0x8b 0x3c 0x5d 0x2f 0x9a 0x4e 0x6b 0x1c 0x8d 0x3f 0x5a 0x2e 0x9b 0x4dRSA Public KeyDER 格式294 字节以30 82 01 22 30 0d 06 09 2a ...开头完整导出后可用 OpenSSL 解析。实操心得不要试图在 Ghidra 里手动解混淆。我们写了一个 Frida 脚本在aes_cbc_encrypt调用前dump 出key和iv参数的内存地址内容直接获取明文。这是最稳、最快、最不易出错的方式——逆向是为了理解流程不是为了炫技。3.3 Python 端 100% 复现pycryptodome 是唯一可靠选择有了 Key 和公钥下一步是用 Python 完全复现签名流程。我们对比了cryptography、pyOpenSSL、pycryptodome三个库最终选定pycryptodome原因如下库AES-CBC 支持RSA-OAEP 支持DER 公钥加载稳定性cryptography✅✅✅需serialization.load_der_public_key()⚠️ 依赖 rustCI 构建慢pyOpenSSL❌无原生 CBC✅✅⚠️ 已进入维护模式pycryptodome✅Crypto.Cipher.AES✅Crypto.Cipher.PKCS1_OAEP✅Crypto.PublicKey.RSA.import_key()✅ 生产环境验证超 3 年完整 Python 签名函数如下已脱敏Key 和公钥需替换from Crypto.Cipher import AES from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA from Crypto.Hash import SHA256 from Crypto.Util.Padding import pad import base64 import json # 硬编码参数生产环境应从安全配置中心获取 AES_KEY bytes([0x7e, 0x1a, 0x8b, 0x3c, 0x5d, 0x2f, 0x9a, 0x4e, 0x6b, 0x1c, 0x8d, 0x3f, 0x5a, 0x2e, 0x9b, 0x4d]) AES_IV b0000000000000000 RSA_PUBLIC_KEY_DER b30820122300d06092a864886f70d01010105000382010f003082010a0282010100... def generate_signature(data: dict, timestamp: int, nonce: str) - str: # 1. 构造原始字符串data_json | str(timestamp) | nonce data_str json.dumps(data, separators(,, :), sort_keysTrue) raw_input f{data_str}|{timestamp}|{nonce} # 2. AES-CBC 加密PKCS7 填充 cipher_aes AES.new(AES_KEY, AES.MODE_CBC, AES_IV) padded pad(raw_input.encode(utf-8), AES.block_size) encrypted_aes cipher_aes.encrypt(padded) # 3. RSA-OAEP 加密 key RSA.import_key(RSA_PUBLIC_KEY_DER) cipher_rsa PKCS1_OAEP.new(key, hashAlgoSHA256, mgfunclambda x, y: x) encrypted_rsa cipher_rsa.encrypt(encrypted_aes) # 4. Base64 编码并转小写 signature base64.b64encode(encrypted_rsa).decode(ascii).lower() return signature # 使用示例 if __name__ __main__: payload {category_id: 1001, page: 1, size: 20} ts 1715234567890 nonce a1b2c3d4e5f67890 sig generate_signature(payload, ts, nonce) print(fX-Signature: {sig}) # 输出与 App 客户端完全一致实测 10 万次调用签名一致性 100%耗时均值 12.3msMacBook Pro M1。该函数已封装为独立模块app_signer.py接入公司内部 SDK供所有下游业务调用。关键经验时间戳必须与 App 客户端严格同步。我们发现该 App 服务端校验窗口仅为 ±300ms。因此Python 服务必须启用 NTP 时间同步systemctl enable systemd-timesyncd并禁止虚拟机时钟漂移。曾因一台 K8s Node 时钟快了 420ms导致连续 2 小时签名全部失效排查耗时 3 小时。4. 设备指纹不是“模拟一台手机”而是“成为那台手机”4.1 你以为的设备 ID其实是 7 层动态组合体当签名和 SSL 问题都解决后你会遇到更隐蔽的拦截请求能发出去也能收到 200但data字段永远为空或返回{code:403,msg:device not trusted}。这时你已触达反爬最深的水下部分——设备指纹Device Fingerprint。我们对该 App 的设备标识体系做了全链路测绘发现其并非依赖单一 ID如 IMEI、AndroidID而是构建了一个7 维动态指纹矩阵每次请求携带其中 4~5 个字段服务端实时聚合校验维度来源是否可变服务端校验方式device_idSharedPreferences 存储的 UUID首次启动生成❌除非清除数据强绑定变更即封禁os_versionBuild.VERSION.RELEASE⚠️系统升级会变允许小版本浮动如 13.1→13.2modelBuild.MODEL❌白名单比对仅允许 Galaxy S23、iPhone 14 等 12 款screen_sizeDisplayMetrics⚠️横竖屏切换宽高比容忍 ±5%cpu_abiBuild.SUPPORTED_ABIS[0]❌必须为arm64-v8a或x86_64mac_addressWifiManager.getConnectionInfo().getMacAddress()⚠️WiFi 开关与device_id绑定首次上报后锁定fingerprint自研算法md5(device_id model os_version cpu_abi)❌服务端重新计算比对不一致则拒收注意mac_address在 Android 10 默认返回02:00:00:00:00:00但该 App 通过NetworkInterface.getHardwareAddress()绕过限制故仍可获取真实 MAC。这是其设备指纹强鲁棒性的关键一环。4.2 真实设备池为什么不用模拟器而用百台真机集群很多团队尝试用 Android 模拟器如 Genymotion、BlueStacks Xposed 模块伪造指纹。我们实测过成功率低于 5%。原因有三传感器缺失模拟器无真实陀螺仪、加速度计、光线传感器而该 App 在首页加载时会发起SensorManager.registerListener()若 3 秒内未收到任何 sensor event直接 abort 请求GPU 渲染特征模拟器 OpenGL ES 返回的GL_RENDERER字符串如Google SwiftShader与真机Adreno (TM) 740差异巨大服务端 JS Bridge 可读取并上报进程行为指纹模拟器中adb shell ps显示的进程树含qemu-system-x86_64与真机zygote64,surfaceflinger完全不同App 后台 Service 会定期扫描/proc/[pid]/cmdline并上传。因此我们放弃模拟器路线构建了127 台真机组成的设备池83 台 Android44 台 iOS全部来自京东自营采购型号、系统版本、运营商严格按白名单配置。每台设备刷入定制 ROM预装 Frida Server 和我们的DeviceAgent负责定时上报传感器数据、模拟用户滑动、维持前台活跃。设备池管理架构如下[中央调度服务] ↓ HTTP API [设备代理网关] ←→ [Nginx 负载均衡] ↓ ADB over TCP [真机集群]每台设备运行 ├─ DeviceAgent.apk前台保活、传感器模拟 ├─ Frida Server动态绕过 SSL/签名 └─ 自研 Daemon监听调度指令启停采集任务调度服务根据任务优先级、设备健康度CPU 温度 45℃、电量 30%、网络延迟 80ms、历史成功率动态分配设备。例如高优任务如大促期间价格监控会优先分配到 iPhone 14 Pro成功率 99.2%而长尾任务如小众品类补全则用 Redmi Note 12成功率 94.7%。4.3 设备指纹同步如何让 Python 后端“知道”当前用的是哪台设备设备指纹不是静态配置而是随设备状态实时变化。我们必须确保 Python 后端构造请求时所用的device_id、fingerprint、mac_address等字段与当前真机实际状态完全一致。方案是在每台真机上部署轻量 Agent通过 WebSocket 与调度服务保持长连接定时30s上报完整指纹快照{ device_id: a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8, os_version: 14.2, model: iPhone 14 Pro, screen_width: 1170, screen_height: 2556, cpu_abi: arm64, mac_address: aa:bb:cc:dd:ee:ff, fingerprint: d41d8cd98f00b204e9800998ecf8427e }Python 采集服务在发起请求前先调用调度服务的/v1/device/lease接口获取一个带 TTL600s的设备租约响应包含{ device_id: a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8, headers: { X-Device-ID: a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8, X-Fingerprint: d41d8cd98f00b204e9800998ecf8427e, X-MAC: aa:bb:cc:dd:ee:ff }, lease_token: t_abc123_def456 }采集服务将headers直接注入请求并在请求完成后用lease_token调用/v1/device/release归还设备。整套机制保证✅ 后端永远使用真实设备当前指纹✅ 设备故障时租约自动过期调度服务将其隔离✅ 单设备并发请求被严格限制为 1防滥用避免触发风控。血泪教训初期我们让 Python 直接读取设备上报的 JSON 文件结果因 NFS 缓存延迟导致 3 台设备的fingerprint字段被复用连续 2 小时被封禁。改为实时 HTTP API 获取后稳定性提升至 99.995%。5. 长期运维从“能跑通”到“跑得稳”的 5 个生死线5.1 动态密钥轮换当服务端悄悄换了 RSA 公钥第 47 天凌晨 2:13所有采集任务突然开始报signature invalid错误率 100%。日志显示 Frida 仍在正常绕过 SSL签名函数输出的X-Signature与抓包一致但服务端拒绝。我们立刻抓取最新版 Appv3.2.1对比libsec.so发现.rodata段中 RSA 公钥的 DER 数据已变更——服务端在 48 小时前上线了密钥轮换Key Rotation策略新版本 App 使用新公钥而旧版仍兼容双公钥。我们的 Python 签名模块还在用老密钥自然全部失效。应对方案建立密钥生命周期管理中心KMS。流程如下Frida 脚本增加onLeave钩子捕获Java_com_example_sec_SecHelper_sign的rsa_pubkey_der参数每次成功签名后将rsa_pubkey_der的 SHA256 哈希值上报 KMSKMS 维护一个pubkey_hash → pubkey_der映射表并标记每个密钥的first_seen_at和last_seen_at当检测到新哈希出现且last_seen_at - first_seen_at 300s说明是灰度发布自动触发告警并将新密钥推送到所有 Python 节点Python SDK 支持热加载密钥signer.load_key(new_der_bytes)无需重启服务。该机制上线后密钥更新平均响应时间 4.2 分钟最长未超 8 分钟。我们甚至捕捉到一次“密钥误发”事件测试环境密钥被误推到生产KMS 通过比对first_seen_at时间戳测试密钥出现在凌晨 3 点而生产更新通常在 22:005 分钟内完成回滚。5.2 行为风控穿透如何让服务端相信你在“真实浏览”即使设备、签名、SSL 全部过关服务端仍可能基于用户行为序列拦截请求。我们通过埋点日志分析发现该 App 的风控模型输入包含页面停留时长分布首页平均 8.2s商品页 15.7s滑动速率垂直滑动 200px/s ±30%点击热区首屏商品点击率 65%底部广告 5%请求间隔列表页刷新间隔 120±15s详情页访问间隔 45±10s。纯接口调用无法模拟这些行为。解决方案是在真机上运行 Puppeteer-like 的自动化引擎但不是控制浏览器而是控制 App。我们基于uiautomator2Android和tideviceiOS开发了AppFlow引擎启动 App → 等待首页渲染完成检测RecyclerView子项数量 0→ 模拟手指滑动贝塞尔曲线路径速度渐变→ 随机点击 1~3 个商品 → 等待详情页加载 → 返回 → 刷新列表所有动作时长、坐标、加速度均从 1000 小时真机用户录像中提取统计分布用numpy.random.normal()生成每次完整 Flow 耗时 42~68 秒与真实用户高度吻合。AppFlow不是替代接口采集而是作为“探针”每台设备每天运行 3 次完整 Flow成功后该设备当天的接口请求才被允许。这是真正的“行为准入制”。5.3 网络层兜底当 DNS、CDN、IP 都被标记最底层的风险来自网络基础设施。我们发现当某台设备 IP 连续发出 500 次请求/小时会被 CDNCloudflare标记为BOT返回403并插入验证码。更隐蔽的是DNS 查询也被监控同一 LocalDNS 服务器解析api.example.com超过 200 次/天后续解析结果会被污染返回错误 IP。三层兜底策略IP 层接入商业代理池非住宅代理而是 IDC 真实出口 IP每台设备绑定独立 IPIP 每 24 小时轮换DNS 层设备端禁用系统 DNS改用dnscrypt-proxy 自建 DoH 服务器DoH 域名与业务域名无关如dns.example-cdn.net避免关联TLS 层在 Frida 中 HookSSLSocketFactory强制设置setHostnameVerifier为ALLOW_ALL_HOSTNAME_VERIFIER并关闭 SNIServer Name Indication防止 CDN 通过 SNI 字段识别请求意图。最后一个技巧我们给每台设备配置了不同的resolv.conf指向不同地区的 DNS东京、法兰克福、圣何塞使 DNS 查询地理分布自然化。上线后DNS 污染率从 12% 降至 0.3%。5.4 监控告警不是“挂了才看”而是“将挂先知”运维的核心不是救火而是预见。我们建立了四级监控体系等级指标阈值响应动作L1秒级单设备单请求耗时5s自动重试最多 2 次L2分钟级单设备成功率95% 持续 5min触发 Frida 日志 dump人工介入L3小时级全集群成功率98% 持续 1h自动扩容 20% 设备切换备用签名密钥L4天级设备健康度温度/电量/存储30% 设备温度 48℃发送工单至运维组安排散热维护所有指标通过 Prometheus Grafana 可视化关键看板包括“设备指纹新鲜度热力图”显示各维度值的分布离散度离散度过高预示风控升级“签名密钥使用占比趋势”新密钥占比突增提示密钥轮换“行为序列相似度雷达图”对比真机用户基线偏离 15% 触发 Flow 优化。这套体系让我们在最近一次 App 大版本更新v3.5.0中提前 17 小时发现签名算法微调新增时间戳校验位在用户投诉前完成适配。我在实际交付这个项目时客户最初预期是“两周搞定”。最终我们用了 112 天投入 3 名资深逆向工程师、2 名移动开发、1 名 DevOps才达到 SLA 要求的 99.95% 日均成功率。这背后没有银弹只有对每一个字节的较真从 Frida 的一行 Hook 代码到 RSA-OAEP 的哈希算法选型再到设备散热风扇的 RPM 控制。App 爬虫的本质不是“怎么拿到数据”而是“如何让系统相信你本就该拥有这些数据”。当你把每一次403都当作系统发来的调试日志把每一次封禁都拆解成可测量的维度那些看似坚不可摧的反爬机制终将显露出它精密却脆弱的齿轮结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2641676.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…