Windows API逆向:常用API的调用识别与参数分析

做逆向分析,说白了就是跟操作系统打交道。而Windows API,就是系统给我们开的门。你逆向一个PE文件,十有八九会遇到MessageBox、CreateFile、ReadFile、WriteFile这几个老朋友。今天我就带你把这些API的调用特征、参数结构、以及逆向时的识别技巧,一次性讲透。

一、MessageBox:最显眼的断点

MessageBox这个API,我愿称之为逆向入门的第一道门槛。为什么?因为它太显眼了。你随便拖一个恶意软件样本进IDA,看到弹窗,十有八九就是它。

函数原型:

int MessageBox(
  HWND    hWnd,      // 父窗口句柄,可为NULL
  LPCTSTR lpText,    // 消息文本
  LPCTSTR lpCaption, // 标题文本
  UINT    uType      // 按钮和图标类型
);

在x86环境下,参数从右往左压栈。你看到连续四个push指令,然后一个call,基本就能锁定。我个人习惯在OD或x64dbg里直接下断点:bp MessageBoxW。注意,有W和A两个版本,W是Unicode,A是ANSI。现在大部分程序都用W版本。

实战技巧:我在分析一个勒索软件时,发现它弹窗显示"你的文件已被加密"。直接下断MessageBoxW,堆栈里就能看到字符串地址。顺着字符串引用往上翻,很快就找到了加密逻辑的入口。这招屡试不爽。

二、CreateFile:文件操作的起点

CreateFile这个名字有点误导人。它不光能创建文件,还能打开文件、管道、甚至物理设备。逆向时看到这个API,意味着程序要开始读写数据了。

HANDLE CreateFile(
  LPCTSTR               lpFileName,      // 文件名
  DWORD                 dwDesiredAccess, // 访问模式
  DWORD                 dwShareMode,     // 共享模式
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性
  DWORD                 dwCreationDisposition, // 创建方式
  DWORD                 dwFlagsAndAttributes,  // 文件属性
  HANDLE                hTemplateFile    // 模板文件句柄
);

参数分析时,我最关注的是dwDesiredAccessdwCreationDisposition。前者告诉你程序要读还是写,后者告诉你它是新建文件还是打开已有文件。

dwDesiredAccess 常见值 含义
GENERIC_READ (0x80000000) 只读
GENERIC_WRITE (0x40000000) 只写
GENERIC_READ | GENERIC_WRITE 读写
dwCreationDisposition 常见值 含义
OPEN_EXISTING (3) 打开已有文件
CREATE_ALWAYS (2) 总是新建
OPEN_ALWAYS (4) 存在则打开,否则新建

嗯,这里要注意。如果看到CREATE_ALWAYS配合GENERIC_WRITE,基本可以断定程序在写新文件。我曾经分析过一个后门程序,它就是用这种方式在临时目录下释放DLL文件。

三、ReadFile 与 WriteFile:数据搬运工

这两个API通常成对出现。一个读,一个写。逆向时看到它们,意味着数据在流动。

BOOL ReadFile(
  HANDLE       hFile,                // 文件句柄
  LPVOID       lpBuffer,             // 接收数据的缓冲区
  DWORD        nNumberOfBytesToRead, // 要读取的字节数
  LPDWORD      lpNumberOfBytesRead,  // 实际读取的字节数
  LPOVERLAPPED lpOverlapped          // 重叠I/O结构
);

BOOL WriteFile(
  HANDLE       hFile,                // 文件句柄
  LPCVOID      lpBuffer,             // 要写入的数据
  DWORD        nNumberOfBytesToWrite,// 要写入的字节数
  LPDWORD      lpNumberOfBytesWritten,// 实际写入的字节数
  LPOVERLAPPED lpOverlapped          // 重叠I/O结构
);

参数分析的核心在于lpBuffernNumberOfBytesToRead/Write。缓冲区地址指向哪里?读/写多少字节?这些信息直接告诉你程序在操作什么数据。

避坑指南:我曾经在分析一个加密程序时,看到它ReadFile读取了0x1000字节,然后调用了一个自定义函数。我一开始以为那个函数是加密算法,结果跟进去发现只是个memcpy。真正的加密逻辑在更后面。所以,别被API调用顺序骗了,一定要跟踪数据流。

四、调用识别:静态与动态结合

识别API调用,我一般分两步走:

  1. 静态分析:在IDA里看导入表(IAT)。如果看到user32.dll里有MessageBoxW,kernel32.dll里有CreateFileW,那基本八九不离十。然后交叉引用(XREF)找到调用点。
  2. 动态调试:在OD或x64dbg里下断点。观察堆栈参数,确认实际传入的值。有时候静态分析看到的参数是变量,动态才能看到具体数值。

你想想看,静态分析就像看地图,动态调试就像亲自走一遍。两者结合,才能把API调用彻底搞清楚。

五、参数分析的实战技巧

参数分析,说白了就是回答三个问题:

  • 谁在调用?—— 看调用者的上下文
  • 传了什么?—— 看堆栈或寄存器
  • 返回值怎么用?—— 看后续代码

举个例子。你在x64dbg里断在CreateFileW,堆栈显示:

0019F8E0  | 00401000  | lpFileName = "C:\config.ini"
0019F8E4  | 80000000  | dwDesiredAccess = GENERIC_READ
0019F8E8  | 00000001  | dwShareMode = FILE_SHARE_READ
0019F8EC  | 00000000  | lpSecurityAttributes = NULL
0019F8F0  | 00000003  | dwCreationDisposition = OPEN_EXISTING
0019F8F4  | 00000080  | dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL
0019F8F8  | 00000000  | hTemplateFile = NULL

看到没?程序以只读方式打开一个已有的配置文件。接下来大概率会调用ReadFile读取内容。这时候你可以在ReadFile上下断,看看它读了多少数据,存到了哪里。

我的习惯:在分析未知样本时,我会先给所有文件操作API下断点。然后运行程序,观察它依次调用了哪些API。这样能快速勾勒出程序的行为轮廓。比如:CreateFile -> ReadFile -> 一些计算 -> WriteFile -> CloseHandle。这基本就是一个文件处理流程。

六、知识体系总览

下面这张图,是我梳理的Windows API逆向核心逻辑。你看一眼,心里就有谱了。

Windows API 逆向分析核心流程 1. API识别 导入表(IAT) / 断点 2. 参数分析 堆栈/寄存器/常量 3. 行为推断 读/写/弹窗/网络 MessageBox 弹窗/提示/交互 CreateFile 打开/创建文件 ReadFile 读取数据 WriteFile 写入数据 参数分析要点 • hWnd/lpFileName:指向什么资源? • dwDesiredAccess/dwCreationDisposition:读还是写?新建还是打开? • lpBuffer:缓冲区地址在哪?数据流向何方? • nNumberOfBytes:操作了多少数据?是否与后续计算相关?

这张图把整个流程串起来了。从API识别开始,到参数分析,再到行为推断。四个常用API各自对应不同的操作场景。你只要记住这个框架,遇到任何样本都能快速上手。

七、写在最后

Windows API逆向,说白了就是跟操作系统对话。你读懂了API调用,就读懂了程序想干什么。MessageBox告诉你它想跟你交互,CreateFile告诉你它要动文件,ReadFile和WriteFile告诉你数据在流动。

我个人觉得,逆向分析最核心的能力不是记住所有API,而是学会观察和推理。看到API调用,问自己三个问题:它为什么调这个API?参数是什么?返回值怎么用?想清楚这三个问题,你就已经掌握了80%的分析工作。

嗯,今天就聊到这里。下次你遇到一个陌生样本,不妨先从这几个API入手。相信我,你会打开新世界的大门。


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