4、固件静态分析:字符串分析、硬编码凭据检测、敏感函数识别、大模型辅助代码审计

静态分析,说白了就是不动手跑代码,直接看源码或二进制来挖漏洞。我做了这么多年固件安全,发现很多新手上来就喜欢搞动态调试,觉得那样才「硬核」。但说实话,静态分析才是固件安全的基础功——你连代码里写了什么都搞不清楚,动态调试也只会像无头苍蝇。

这一章,我们聚焦四个核心方向:字符串分析、硬编码凭据检测、敏感函数识别,以及大模型辅助代码审计。嗯,前三个是老手艺,最后一个是我最近两年玩得最多的新玩具。

4.1 字符串分析:从文本里挖线索

固件里的字符串,就像犯罪现场的指纹。你想想看,开发者写代码时留下的日志、错误提示、路径信息,甚至注释,都可能暴露关键信息。

我个人习惯,拿到固件第一件事就是跑 strings 命令。别小看这个操作,我曾经在一个路由器的固件里,通过 strings 直接找到了后台管理员的默认密码——就写在字符串里,连加密都没有。

核心思路: 字符串分析不是简单地把文本列出来,而是要有针对性地筛选。

我一般会关注这几类字符串:

  • 路径信息: 比如 /etc/passwd/tmp//var/log/,这些能帮你了解固件的文件系统结构。
  • 错误消息:Login failedConnection timeout,这些往往对应着代码中的关键逻辑分支。
  • 版本号与签名: 比如 v2.1.3Copyright 2023,能帮你确认固件的来源和版本。
  • IP地址与域名: 硬编码的C2服务器地址,或者更新服务器的URL,都是高危线索。

举个例子,我曾在某款智能摄像头的固件里,用 strings 发现了一个隐藏的调试接口路径:/debug/console。访问后直接拿到了root shell。你说这算不算捡漏?但捡漏的前提是,你得知道往哪个方向看。

小技巧:strings -n 6 只显示长度大于等于6的字符串,能过滤掉大量无意义的短字符。配合 grep 使用效果更佳,比如 strings firmware.bin | grep -i "password\|secret\|key"

4.2 硬编码凭据检测:别让密码裸奔

硬编码凭据,是固件安全里最常见的漏洞之一。说白了,就是开发者把密码、密钥、Token直接写死在代码里。我见过最离谱的一次,某厂商的固件里,WiFi密码、云平台API Key、甚至数据库连接字符串,全在一个头文件里明文存储。

为什么会这样?很多时候是为了开发方便,测试阶段写死了,上线时忘了改。或者,有些厂商觉得「固件是封闭的,没人能看」,这种想法在2010年可能还行得通,现在嘛……

检测硬编码凭据,我一般分三步走:

  1. 关键词匹配: 搜索 passwordpwdsecretkeytokenauth 等常见关键词。
  2. 模式识别: 比如 char *pwd = "xxxx" 这种赋值语句,或者 #define PASSWORD "xxxx" 宏定义。
  3. 熵值分析: 高熵值的字符串(看起来像乱码的)可能是加密密钥或哈希值,需要重点关注。

这里我分享一个我常用的Python脚本片段,用来快速扫描固件中的硬编码凭据:

import re
import sys

def scan_hardcoded_creds(file_path):
    patterns = [
        r'(?i)(password|pwd|secret|key|token|auth)\s*[=:]\s*["\']([^"\']+)["\']',
        r'(?i)#define\s+\w*(PASSWORD|SECRET|KEY)\w*\s+["\']([^"\']+)["\']',
        r'(?i)(api_key|api_secret|access_token)\s*[=:]\s*["\']([^"\']+)["\']',
    ]
    
    with open(file_path, 'rb') as f:
        content = f.read()
        # 只处理可打印字符串
        text = content.decode('utf-8', errors='ignore')
        
        for pattern in patterns:
            matches = re.findall(pattern, text)
            for match in matches:
                print(f"[!] 发现硬编码凭据: {match[0]} = {match[1]}")

if __name__ == "__main__":
    scan_hardcoded_creds(sys.argv[1])
注意: 硬编码凭据检测不能只靠自动化工具。我曾经遇到过一种情况,密码被拆分成多段,分别存储在固件的不同位置,运行时再拼接。这种就需要人工结合逆向分析才能发现。

4.3 敏感函数识别:危险的API调用

固件里有些函数,天生就是高危的。比如 system()popen()exec()sprintf()strcpy() 这些。它们要么能执行系统命令,要么容易导致缓冲区溢出。

我识别敏感函数的方法很简单:先反汇编或反编译固件,然后搜索函数名。但这里有个坑——很多固件会静态链接库,函数名可能被strip掉。这时候就得靠特征码匹配了。

举个例子,system() 函数在ARM架构下的调用方式通常是 BL system,但如果没有符号表,你可以通过搜索 /bin/sh 字符串来定位——因为 system() 内部会调用 /bin/sh -c

我整理了一个常用的敏感函数清单,供你参考:

函数名 风险类型 典型场景
system() 命令注入 执行外部命令,参数可控时可直接提权
popen() 命令注入 与system类似,但可获取命令输出
sprintf() 格式化字符串/缓冲区溢出 未限制输出长度,容易覆盖栈
strcpy() 缓冲区溢出 不检查目标缓冲区大小
memcpy() 缓冲区溢出 长度参数由用户控制时风险极高
gets() 缓冲区溢出 标准C库中最危险的函数之一

嗯,这里要注意,光找到敏感函数还不够,你得看它的参数是否可控。比如 system("ls") 是安全的,但 system(user_input) 就是高危。我曾经在分析某款智能门锁固件时,发现 system() 的参数直接拼接了用户输入的WiFi SSID,结果你懂的——任意命令执行。

4.4 大模型辅助代码审计:AI时代的利器

好了,终于到了我最想聊的部分。大模型辅助代码审计,说白了就是让AI帮你读代码、找漏洞。我这两年用下来,感觉这东西就像一把双刃剑——用好了效率翻倍,用不好反而被误导。

先说说大模型能干什么:

  • 快速理解代码逻辑: 把一段反编译后的C代码扔给大模型,它能帮你总结出这段代码是干什么的。比如「这是一个处理HTTP请求的函数,其中第45行存在潜在的整数溢出风险」。
  • 识别漏洞模式: 大模型训练时见过大量漏洞样本,它能识别出常见的漏洞模式。比如 memcpy(dst, src, user_len) 这种,它会直接告诉你「这里可能缓冲区溢出」。
  • 生成PoC: 有些大模型甚至能帮你生成漏洞验证代码。不过这个我建议谨慎使用,毕竟生成的东西不一定能跑通。

我举个例子。有一次我在分析一个IoT设备的固件,遇到一段反编译后的代码,逻辑非常绕。我花了半小时没看懂,后来把代码贴给大模型,它10秒钟就给出了分析:「这是一个命令解析函数,支持5种命令类型,其中CMD_TYPE_3存在未授权访问漏洞,因为权限检查在命令解析之后才执行。」

你看,这就是AI的价值——不是替代你,而是帮你节省时间。

我的建议: 大模型辅助审计的正确姿势是「人机协作」。AI负责初筛和总结,你负责验证和深入分析。千万别盲目相信AI的输出,它也会犯错。

下面是我总结的一个大模型辅助审计的工作流:

1. 提取固件中的关键代码片段(反编译后的C代码或汇编)
2. 将代码片段输入大模型,要求它:
   - 总结代码功能
   - 标注所有输入点
   - 识别潜在的安全风险
   - 给出风险等级(高/中/低)
3. 人工验证AI的输出:
   - 检查是否有漏报(AI没发现的漏洞)
   - 检查是否有误报(AI说有问题但实际上安全的)
   - 对高风险项进行深入逆向分析
4. 输出审计报告

这里我画了一张图,展示整个静态分析的知识体系:

固件静态分析知识体系 固件静态分析 字符串分析 硬编码凭据检测 敏感函数识别 大模型辅助审计 路径信息提取 错误消息分析 版本/签名识别 关键词匹配 模式识别 熵值分析 命令执行函数 缓冲区操作函数 格式化字符串函数 代码逻辑理解 漏洞模式识别 PoC生成 人机协作:AI初筛 + 人工验证 自动化工具提升效率,但最终决策依赖专家经验

最后,我想说一句:大模型是个好工具,但它不是万能的。我见过有人把整个固件扔给AI,然后直接拿AI的报告去交差,结果漏掉了最关键的漏洞。记住,AI是辅助,你才是专家。

避坑指南: 我曾经用大模型分析一段固件代码,AI信誓旦旦地说「第88行存在栈溢出漏洞」,我仔细一看,发现那个变量其实是在堆上分配的。所以,AI的分析一定要人工复核,尤其是涉及内存布局和调用约定的地方。

好了,静态分析这部分就聊到这里。下一章我们会进入动态分析的领域,到时候再聊怎么用调试器和模拟器来验证静态分析的发现。


公众号:蓝海资料掘金营,微信deep3321