本文将指导你构建一个适用于Windows平台下x86/x64 CPU环境的静态分析框架。我们将探讨该框架的核心流程,并与IDA Pro的基础功能进行对比。这个框架是实现诸如空闲寄存器分析、基本块分析、控制流图(CFG)分析、代码混淆乃至污点分析等高级静态分析功能的基础。
本文聚焦于三个核心模块的构建:
- 指令流构建 (
buildInstFlow)
- 基本块构建 (
buildBasicBlock)
- 控制流图构建 (
buildCFG)
在深入实现之前,需要了解一些基本概念,这对理解后续的算法与数据结构设计至关重要。
必要概念

CFG (控制流图)
控制流图是程序分析和编译器优化中的基础数据结构,用于表示程序执行过程中所有可能的控制流路径。它将程序的执行流程抽象为一个有向图,其中节点代表基本块,边代表控制流转移。
形式化定义为一个有向图 G = (N, E, Entry, Exit),其中:
- N:节点集合,每个节点代表一个基本块
- E ⊆ N × N:边集合,表示控制流转移关系
- Entry:唯一的入口节点
- Exit:一个或多个出口节点
BasicBlock (基本块)
基本块是编译器和程序分析中的基本单元,是一段顺序执行的代码序列,具有以下特点:
- 单入口:只能从第一条指令进入
- 单出口:只能从最后一条指令离开
- 原子执行:要么全部执行,要么都不执行
一个基本块 BB 是一个指令序列 {i₁, i₂, ..., iₙ},满足:
- 单入口性质:只有 i₁ 可以作为执行的起点
- 单出口性质:只有 iₙ 可以将控制流转移到其他基本块
- 顺序执行性质:i₁ → i₂ → ... → iₙ 顺序执行,中间无分支
基本块划分原则
划分规律如下:
- 程序的第一条指令
- 任何跳转指令的目标指令
- 紧跟在跳转指令后面的指令
IDA F5 原理浅析
虽然本文的框架看似与IDA的F5反编译功能相去甚远,但实则完成了F5功能的第一步。IDA通常遵循以下步骤:
- PE结构扫描分析
- 函数识别
- 将汇编提升为平台无关的 LLIL (低级中间语言)
- 将 LLIL 提升至 MLIL (中级中间语言),并进行函数及参数解析
- 优化并根据上一步结果进行语义翻译,生成伪代码
本文设计的框架采用了非SSA的标记汇编作为简化的LLIL表示(严格意义上并非标准LLIL,仅为教学方便),并完成了上述第3步,同时构建了CFG和基本块划分。
LLIL (低级中间语言)
LLIL是一种用于程序分析和逆向工程的中间表示形式。它将机器码抽象为更易于分析的形式,同时保留了底层的语义细节,其核心目标之一是架构无关性。
如何设计 LowLevelIL
关于设计LLIL,可以总结为五个精炼原则:
- 语义明确性
- 架构无关性
- 结构统一性
- 信息完整性
- 可验证性
在本文的简化设计中,我们暂舍弃架构无关性。以下给出一个简化的IL设计格式。在实际构建中,可以利用Zydis、Capstone等反汇编框架来辅助实现,这比手动实现复杂的转换引擎更具可操作性。
; L_xxx 是用于标记指令地址的常规标签
L_xxx:
mov eax, ebx
; J_xxx 是跳转目标的标签
J_xxx:
mov eax, ebx
通过常见的反汇编引擎可以轻易实现上述标签标记。处理之后,即可将反汇编文件格式转换为我们需要的LLIL表示。

框架流程与对象设计
首先需要明确静态分析所需的核心对象并进行设计,主要包括:
- 指令对象
CBasicInst
- 基本块对象
CBasicBlock
以下是示例代码:
enum class InstType {
NORMAL, // 普通指令
BRANCH, // Jxx 条件跳转
JUMP, // 无条件跳转 (jmp)
CALL, // 函数调用
RET // 返回指令
};
class CBasicInst {
public:
CBasicInst() : type(InstType::NORMAL) {}
/* 汇编代码,如 mov eax, ebx */
std::string m_strAsmCode;
/* 操作码,如 mov, je, jb */
std::string m_strOpcode;
/* 操作数,如 eax, ebx */
std::string m_strOperands;
/* 指令标签 */
std::string m_strLabel;
/* 指令类型 */
InstType type;
/* 空闲寄存器集合,用于活变量分析 */
std::set<std::string> m_setFreeReg;
/* 跳转目标地址 */
std::string m_strJmpTarget;
/* 判断是否为基本块结束语句 */
bool isBlockTerminator() const {
return type == InstType::BRANCH ||
type == InstType::JUMP ||
type == InstType::RET;
}
};
class CBasicBlock {
public:
/* 块起始标签,通常取第一个指令的标签 */
std::string m_strBlockLabel;
/* 指令序列 */
std::vector<CBasicInst> m_instructions;
/* 后继块标签集合 */
std::set<std::string> successors;
/* 前驱块标签集合 */
std::set<std::string> predecessors;
/* 添加指令 */
void addInstruction(const CBasicInst& inst) {
m_instructions.push_back(inst);
}
/* 获取第一条指令的标签 */
std::string getStartLabel() const {
return m_instructions.empty() ? "" : m_instructions.front().m_strLabel;
}
/* 获取块标签 */
std::string getBlockLable() const {
return getStartLabel();
}
// 获取最后一条指令
const CBasicInst* getLastInst() const {
return m_instructions.empty() ? nullptr : &m_instructions.back();
}
/* 获取块大小(指令数量) */
unsigned int getBlockSize() {
return m_instructions.size();
}
/* 判断是否为空块 */
bool isEmpty() {
return m_instructions.empty();
}
};
框架流程严格按照上文所述依次构建:指令流 -> 基本块 -> 控制流图。以下是核心流程的示意图:
指令流构建 (buildInstFlow)

基本块构建 (buildBasicBlock)

控制流图构建 (buildCFG)

原始指令流提取
bool CAssemblyScanner::buildInstFlow() {
if (m_strFileName == "") {
std::cerr << "Error: Filename not empty " << m_strFileName << std::endl;
return false;
}
/* 读取所有行 */
std::vector<std::string> lines;
if (!readShellcodeSection(lines)) {
return false;
}
/* 通过行构建原始指令流 */
m_vecRawInsts.clear();
std::string currentLabel = ""; // 当前待分配的标签
for (auto& line : lines) {
std::string trimmedLine = trim(line);
if (trimmedLine.empty()) continue;
/* 判断是否为标签行:冒号必须在行尾 */
if (!trimmedLine.empty() && trimmedLine.back() == ':') {
// 提取标签(去除末尾冒号)
currentLabel = trimmedLine.substr(0, trimmedLine.length() - 1);
currentLabel = trim(currentLabel); // 去除可能的空白
} else {
/* 提取指令 */
CBasicInst inst;
if (parseInstruction(trimmedLine, inst)) {
inst.m_strLabel = currentLabel;
inst.m_strAsmCode = trimmedLine;
m_vecRawInsts.push_back(inst);
currentLabel = "";
}
}
}
m_bParsed = true;
return true;
}
构建BasicBlock
bool CAssemblyScanner::buildBasicBlock() {
if (!m_bParsed) {
std::cerr << "Error: Must call buildInstFlow() first!" << std::endl;
return false;
}
if (m_vecRawInsts.empty()) {
std::cerr << "Error: No instructions to build blocks" << std::endl;
return false;
}
// ========== 第一步:收集所有跳转目标标签 ==========
std::set<std::string> jumpTargets;
for (const auto& inst : m_vecRawInsts) {
// 收集所有跳转、分支、调用指令的目标
if ((inst.type == InstType::JUMP ||
inst.type == InstType::BRANCH ||
inst.type == InstType::CALL) &&
!inst.m_strJmpTarget.empty()) {
jumpTargets.insert(inst.m_strJmpTarget);
}
}
// ========== 第二步:标记基本块边界 ==========
std::set<size_t> blockStarts;
// 规则1:第一条指令是基本块入口
blockStarts.insert(0);
for (size_t i = 0; i < m_vecRawInsts.size(); ++i) {
const auto& inst = m_vecRawInsts[i];
// 规则2:跳转目标地址是基本块入口
if (!inst.m_strLabel.empty() && jumpTargets.count(inst.m_strLabel) > 0) {
blockStarts.insert(i);
}
// 规则3:紧跟跳转指令的指令是基本块入口
if (inst.isBlockTerminator() && i + 1 < m_vecRawInsts.size()) {
blockStarts.insert(i + 1);
}
}
// ========== 第三步:按边界切分指令流 ==========
m_vecBlocks.clear();
std::vector<size_t> starts(blockStarts.begin(), blockStarts.end());
for (size_t i = 0; i < starts.size(); ++i) {
size_t startIdx = starts[i];
size_t endIdx = (i + 1 < starts.size()) ? starts[i + 1] : m_vecRawInsts.size();
auto block = std::make_unique<CBasicBlock>();
// 将指令添加到块中
for (size_t j = startIdx; j < endIdx; ++j) {
block->addInstruction(m_vecRawInsts[j]);
}
// 设置块标签:优先使用跳转目标标签
if (!block->m_instructions.empty()) {
std::string labelToUse = "";
// 查找是否有跳转目标标签
for (const auto& inst : block->m_instructions) {
if (!inst.m_strLabel.empty() && jumpTargets.count(inst.m_strLabel) > 0) {
labelToUse = inst.m_strLabel;
break;
}
}
// 如果没有,使用第一条指令的标签
if (labelToUse.empty() && !block->m_instructions[0].m_strLabel.empty()) {
labelToUse = block->m_instructions[0].m_strLabel;
}
// 如果还是没有,生成唯一块标签
if (labelToUse.empty()) {
labelToUse = "BB_" + std::to_string(m_vecBlocks.size());
}
block->m_strBlockLabel = labelToUse;
}
m_vecBlocks.push_back(std::move(block));
}
// ========== 第四步:建立标签索引 ==========
m_mapLabelToBlock.clear();
m_mapLabelToInst.clear();
for (auto& block : m_vecBlocks) {
// 块标签 -> 块指针
m_mapLabelToBlock[block->m_strBlockLabel] = block.get();
// 所有指令标签 -> 块和指令
for (auto& inst : block->m_instructions) {
if (!inst.m_strLabel.empty()) {
m_mapLabelToInst[inst.m_strLabel] = &inst;
m_mapLabelToBlock[inst.m_strLabel] = block.get();
}
}
}
std::cout << "Built " << m_vecBlocks.size() << " basic blocks" << std::endl;
return true;
}
重建CFG
bool CAssemblyScanner::buildCFG() {
if (m_vecBlocks.empty()) {
std::cerr << "Error: Must call buildBasicBlock() first!" << std::endl;
return false;
}
/* 清空现有关系 */
for (auto& block : m_vecBlocks) {
block->successors.clear();
block->predecessors.clear();
}
/* 遍历基本块分析控制流 */
for (size_t i = 0; i < m_vecBlocks.size(); ++i) {
auto& currentBlock = m_vecBlocks[i];
const CBasicInst* lastInst = currentBlock->getLastInst();
if (!lastInst) {
/* 空块跳过 */
continue;
}
// 根据最后一条指令的类型建立后继关系
switch (lastInst->type) {
case InstType::BRANCH: // 条件跳转:有两个后继
{
// 后继1:跳转目标块
if (!lastInst->m_strJmpTarget.empty()) {
CBasicBlock* targetBlock = findBlockByLabel(lastInst->m_strJmpTarget);
if (targetBlock) {
currentBlock->successors.insert(targetBlock->m_strBlockLabel);
targetBlock->predecessors.insert(currentBlock->m_strBlockLabel);
} else {
std::cerr << "Warning: Branch target not found: "
<< lastInst->m_strJmpTarget << std::endl;
}
}
// 后继2:顺序执行到下一个块 (fallthrough)
if (i + 1 < m_vecBlocks.size()) {
auto& nextBlock = m_vecBlocks[i + 1];
currentBlock->successors.insert(nextBlock->m_strBlockLabel);
nextBlock->predecessors.insert(currentBlock->m_strBlockLabel);
}
break;
}
case InstType::JUMP: // 无条件跳转:只有一个后继
{
if (!lastInst->m_strJmpTarget.empty()) {
CBasicBlock* targetBlock = findBlockByLabel(lastInst->m_strJmpTarget);
if (targetBlock) {
currentBlock->successors.insert(targetBlock->m_strBlockLabel);
targetBlock->predecessors.insert(currentBlock->m_strBlockLabel);
} else {
std::cerr << "Warning: Jump target not found: "
<< lastInst->m_strJmpTarget << std::endl;
}
}
break;
}
case InstType::CALL: // 函数调用:通常返回到下一个块
{
// CALL 指令通常会返回,后继是下一个块(过程内分析)
if (i + 1 < m_vecBlocks.size()) {
auto& nextBlock = m_vecBlocks[i + 1];
currentBlock->successors.insert(nextBlock->m_strBlockLabel);
nextBlock->predecessors.insert(currentBlock->m_strBlockLabel);
}
// 注:这里不处理 CALL 的目标函数内部流程,那是过程间分析(ICFG)的范畴
break;
}
case InstType::RET: // 返回指令:没有后继(过程内)
{
// RET 指令在过程内CFG中没有后继块
break;
}
case InstType::NORMAL: // 普通指令:顺序执行到下一个块
{
if (i + 1 < m_vecBlocks.size()) {
auto& nextBlock = m_vecBlocks[i + 1];
currentBlock->successors.insert(nextBlock->m_strBlockLabel);
nextBlock->predecessors.insert(currentBlock->m_strBlockLabel);
}
break;
}
default:
break;
}
}
std::cout << "CFG built successfully with " << m_vecBlocks.size()
<< " blocks" << std::endl;
return true;
}
输出绘制
以下CFG输出代码示例使用Mermaid语法生成图表。请注意,核心算法建议手动实现,采用“注释驱动编码”的方式有助于提升编码能力。
void CAssemblyScanner::dbgPrintCFG() {
std::ofstream outFile("CFG.md");
if (!outFile.is_open()) {
std::cerr << "Error: Cannot create CFG.md" << std::endl;
return;
}
// 写入 Mermaid 文件头
outFile << "# Control Flow Graph\n\n";
outFile << "```mermaid\n";
outFile << "flowchart TD\n";
// 遍历所有基本块,定义节点
for (size_t i = 0; i < m_vecBlocks.size(); ++i) {
const auto& block = m_vecBlocks[i];
std::string blockLabel = block->m_strBlockLabel;
// 转义特殊字符
std::string safeLabel = blockLabel;
std::replace(safeLabel.begin(), safeLabel.end(), '_', ' ');
// 获取最后一条指令类型
const CBasicInst* lastInst = block->getLastInst();
std::string instTypeStr = "NORMAL";
if (lastInst) {
switch (lastInst->type) {
case InstType::BRANCH: instTypeStr = "BRANCH"; break;
case InstType::JUMP: instTypeStr = "JUMP"; break;
case InstType::CALL: instTypeStr = "CALL"; break;
case InstType::RET: instTypeStr = "RET"; break;
default: instTypeStr = "NORMAL"; break;
}
}
// 定义节点:显示块标签、指令数量、类型
outFile << " " << blockLabel << "[\"" << safeLabel
<< "<br/>Instructions: " << block->m_instructions.size()
<< "<br/>Type: " << instTypeStr << "\"]\n";
// 为不同类型的块设置不同样式
if (lastInst) {
if (lastInst->type == InstType::RET) {
outFile << " style " << blockLabel << " fill:#f99\n"; // 返回块用红色
} else if (lastInst->type == InstType::BRANCH) {
outFile << " style " << blockLabel << " fill:#9f9\n"; // 条件跳转用绿色
} else if (lastInst->type == InstType::JUMP) {
outFile << " style " << blockLabel << " fill:#99f\n"; // 无条件跳转用蓝色
}
}
// 标记入口块
if (i == 0) {
outFile << " style " << blockLabel << " fill:#ff9,stroke:#333,stroke-width:4px\n";
}
}
outFile << "\n";
// 遍历所有基本块,输出边(后继关系)
for (const auto& block : m_vecBlocks) {
const CBasicInst* lastInst = block->getLastInst();
for (const auto& successor : block->successors) {
// 判断边类型并标注
if (lastInst && lastInst->type == InstType::BRANCH) {
// 条件跳转:区分跳转边和fallthrough边
if (!lastInst->m_strJmpTarget.empty()) {
CBasicBlock* targetBlock = findBlockByLabel(lastInst->m_strJmpTarget);
if (targetBlock && targetBlock->m_strBlockLabel == successor) {
// 跳转边 (taken)
outFile << " " << block->m_strBlockLabel
<< " -->|\"jump (taken)\"| " << successor << "\n";
} else {
// fallthrough边 (not taken)
outFile << " " << block->m_strBlockLabel
<< " -->|\"fallthrough\"| " << successor << "\n";
}
} else {
outFile << " " << block->m_strBlockLabel
<< " --> " << successor << "\n";
}
} else if (lastInst && lastInst->type == InstType::JUMP) {
// 无条件跳转
outFile << " " << block->m_strBlockLabel
<< " -->|\"jump\"| " << successor << "\n";
} else {
// 普通顺序执行
outFile << " " << block->m_strBlockLabel
<< " --> " << successor << "\n";
}
}
}
outFile << "```\n\n";
// 输出统计信息和块详情...
outFile.close();
std::cout << "CFG exported to CFG.md" << std::endl;
}
框架效果展示
最后,让我们将框架的分析效果与IDA 9.0生成的基础CFG图进行直观对比。

基本块的构建结果示例:

初始指令流的构建结果示例:

通过实现这样一个静态分析框架,我们不仅深入理解了网络与系统底层程序分析的基本原理(如控制流图的构建),也掌握了构建类似IDA基础功能的核心算法与数据结构,为进一步探索高级逆向工程分析技术打下了坚实基础。