news 2026/1/23 5:12:53

从零实现:使用Ollydbg跟踪内存注入攻击行为

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零实现:使用Ollydbg跟踪内存注入攻击行为

深入内存的暗流:用 OllyDbg 实时追踪一次经典的进程注入攻击

你有没有想过,一个看似无害的记事本程序(notepad.exe),是如何在毫无征兆的情况下突然弹出一个“Hello World”对话框的?这不是魔法,而是典型的内存注入攻击。而我们要做的,不是去发动攻击,而是像一名数字侦探一样,用OllyDbg把整个过程从头到尾看得清清楚楚。

这不仅是一次技术演示,更是一场对 Windows 用户态执行机制的深度解剖。我们将亲手布下监控、触发注入、实时观察内存变化和执行跳转——所有这一切,都发生在你眼前的一块汇编代码区域里。


为什么是 OllyDbg?它凭什么能看见“看不见”的行为?

尽管现在很多人转向 x64dbg 或者 IDA Pro + 调试器组合,但如果你刚入门逆向分析,想快速理解“程序运行时到底发生了什么”,OllyDbg 依然是最好的起点

它不像 WinDbg 那样深陷内核泥潭,也不像 IDA 需要先静态反编译再动调。OllyDbg 是那种你双击就开、附加进程就能看寄存器和堆栈的工具,轻量、直观、响应快。

更重要的是,它的核心能力正好匹配我们今天的任务:

  • 动态拦截 API 调用;
  • 实时查看内存内容与属性;
  • 跟踪 EIP(指令指针)是否跳去了不该去的地方;
  • 单步执行远程线程创建后的第一行代码。

这些正是识别内存注入的关键线索。

⚠️ 注意:OllyDbg 只支持 32 位程序。如果你想跟踪 64 位目标,请使用 x64dbg。但我们今天选择 notepad.exe 这类传统 32 位宿主,正是为了最大化兼容性和可观察性。


内存注入的本质:把“毒药”喂进别人的嘴里

所谓内存注入,说白了就是这么几步:

  1. 找个“健康”的进程当替身(比如notepad.exe);
  2. 拿到它的操作权限(OpenProcess);
  3. 在它体内申请一块“藏身之地”(VirtualAllocEx分配 RWX 内存);
  4. 把恶意代码(Shellcode)偷偷塞进去(WriteProcessMemory);
  5. 让它自己主动执行那段代码(CreateRemoteThread启动新线程);

整个过程就像给一个人注射了一段外来 DNA,让他开始做原本不会做的事。

而我们的任务,就是在受害者还没发病之前,抓住每一个可疑动作。


攻击代码长什么样?一段会说话的 Shellcode

下面这段 C 程序,就是一个最简单的内存注入器:

#include <windows.h> #include <tlhelp32.h> // 调用 MessageBoxA("Hello World") 的 Shellcode unsigned char shellcode[] = "\x6A\x00\x68\x6C\x6C\x20\x20\x68\x6F\x72\x6C\x64\x68\x65\x20\x57" "\x68\x48\x65\x6C\x6C\x8B\xCC\x53\x51\x52\x50\xB8\xE8\xAF\x00\x00" "\xFF\xD0\xC3"; DWORD GetTargetProcessId(const char* processName) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 pe32 = {0}; pe32.dwSize = sizeof(pe32); if (Process32First(hSnapshot, &pe32)) { do { if (strcmp(pe32.szExeFile, processName) == 0) { CloseHandle(hSnapshot); return pe32.th32ProcessID; } } while (Process32Next(hSnapshot, &pe32)); } CloseHandle(hSnapshot); return 0; } int main() { DWORD pid = GetTargetProcessId("notepad.exe"); if (!pid) { printf("[-] Notepad not found.\n"); return -1; } HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!hProc) { printf("[-] Failed to open process.\n"); return -1; } // 在目标进程中分配可执行内存 LPVOID pRemoteMem = VirtualAllocEx(hProc, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!pRemoteMem) { printf("[-] Memory allocation failed.\n"); CloseHandle(hProc); return -1; } // 写入 Shellcode WriteProcessMemory(hProc, pRemoteMem, shellcode, sizeof(shellcode), NULL); // 创建远程线程执行 Shellcode HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)pRemoteMem, NULL, 0, NULL); if (hThread) { printf("[+] Injected successfully! Thread ID: %lu\n", GetThreadId(hThread)); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } else { printf("[-] Remote thread creation failed.\n"); } CloseHandle(hProc); return 0; }

别被那一串\x开头的数据吓到,那只是机器码的十六进制表示。这段 Shellcode 的作用非常明确:调用MessageBoxA显示一个消息框。

真正关键的是后面的四个 API 调用:
OpenProcessVirtualAllocExWriteProcessMemoryCreateRemoteThread
它们构成了现代内存注入的标准模板。


开始跟踪!七步还原攻击全过程

我们现在切换角色:不再是攻击者,而是防御方。我们要用 OllyDbg 去监控notepad.exe,看看当外部程序试图注入时,系统究竟暴露出了哪些痕迹。

第一步:准备干净的实验环境

建议使用一台Windows 7 32位虚拟机(XP 也可),关闭杀软、UAC 和 DEP(便于观察,实战中需注意差异)。打开记事本,并记住它的 PID(可以用任务管理器或命令行tasklist | findstr notepad查看)。

第二步:用 OllyDbg 附加上去

启动 OllyDbg,按Alt+P,找到notepad.exe并点击 Attach。

你会看到 CPU 窗口暂停在某个地址上,EIP 指向当前正在执行的指令。此时程序已被“冻结”,任何后续行为都会被我们捕获。

第三步:设下天罗地网——API 断点

在 OllyDbg 的命令栏输入以下三条断点命令:

bp kernel32.VirtualAllocEx bp kernel32.WriteProcessMemory bp kernel32.CreateRemoteThread

这三个 API 就是我们要盯死的目标。一旦有人调用它们,调试器就会立即中断,让我们有机会检查上下文。

💡 提示:如果发现断点不生效,可能是 API 被重定向到了API-MS-WIN-*DLL 上。可以尝试用模块名加函数名的方式设置:

bp kernel32.VirtualAllocEx
或右键 → “Set breakpoint” → 输入完整路径。


第四步:发动攻击!

回到主机,编译并运行上面的注入器程序。

不出意外的话,OllyDbg 会在几秒内中断——第一个被捕获的通常是VirtualAllocEx


第五步:揪出“非法占地”行为

当命中VirtualAllocEx时,查看堆栈参数:

参数含义
hProcess目标进程句柄(应为 notepad 的有效句柄)
lpAddress请求分配的地址(通常为 NULL,由系统决定)
dwSize分配大小(对照 shellcode 长度判断是否合理)
flAllocationType应为MEM_COMMIT \| MEM_RESERVE
flProtect关键!必须是PAGE_EXECUTE_READWRITE(RWX)

RWX 内存本身就是高危信号。正常程序极少需要同时可写又可执行的页面。DEP 存在就是为了阻止这种行为,但通过VirtualAllocEx可以绕过。

记录返回值(即分配的地址),比如0x003A0000,这是 Shellcode 即将落脚的位置。


第六步:锁定“投毒”现场 ——WriteProcessMemory

接下来,程序会调用WriteProcessMemory

查看堆栈中的lpBuffer参数——这是攻击机本地存放 Shellcode 的地址。你可以跳转过去(Ctrl+G),在“Dump”窗口中看到原始字节:

6A 00 68 6C 6C 20 20 68 ...

选中这些数据,右键 → “Follow in Disassembler”,OllyDbg 会尝试反汇编这段数据。你会发现它解析成了一系列合法的 x86 指令:

push 0 push 'll ' push 'orld' push 'e W' push 'Hell' mov ecx, esp ...

这就是典型的字符串压栈 + 调用 API 的模式。虽然没有符号信息,但经验丰富的分析员一眼就能看出这是在准备调用MessageBoxA


第七步:见证“夺舍”时刻 ——CreateRemoteThread

最后一个断点,也是最关键的一步:CreateRemoteThread

关注lpStartAddress参数——它应该等于前面VirtualAllocEx返回的那个地址(如0x003A0000)。

这意味着:一个新的线程即将从我们刚刚写入的 Shellcode 处开始执行!

此时你可以:

  • 跳转到该地址(Ctrl+G);
  • 在反汇编窗口中确认代码内容;
  • F7单步进入,亲眼看着第一条push 0被执行;
  • 观察堆栈如何逐步构建参数;
  • 最终看到call eax触发MessageBoxA,屏幕上弹出“Hello World”。

这一刻,你不仅看到了攻击成功,还完整复现了控制流劫持的全过程


如何区分“合法”与“恶意”?检测的艺术

当然,不是所有调用WriteProcessMemory的都是攻击。调试器、游戏修改器、性能监控工具也会用这些 API。

所以真正的检测,靠的是上下文关联分析

行为特征恶意可能性
VirtualAllocEx+ RWX +WriteProcessMemory+ 非模块地址执行⚠️ 极高
写入内存的数据熵值高(加密/编码)🔍 高
lpStartAddress指向堆或未映射区域⚠️ 高
来源进程为未知可执行文件或临时目录⚠️ 高
线程创建后迅速退出注入器⚠️ 中高

结合多个指标,才能做出准确判断。


实战技巧:让 OllyDbg 更好用

在真实分析中,还有一些实用技巧可以提升效率:

✅ 开启 API 日志

进入Options → Debugging options → Events,勾选Log API calls。这样每次调用都会记录到日志窗口,方便回溯。

✅ 使用快照恢复环境

配合 VMware/VirtualBox 快照功能,每次测试前一键还原干净状态,避免残留影响。

✅ 绕过反调试

有些高级 Shellcode 会检测是否处于调试环境,例如调用IsDebuggerPresent()

你可以:
- 在该函数入口打补丁:mov eax, 0; ret
- 或使用插件(如 HideDebugger)隐藏调试器痕迹

✅ 结合 Scylla 恢复 IAT

如果 Shellcode 调用了非导入表中的 API(如动态加载user32.MessageBoxA),可用 Scylla 插件帮助识别 API 地址来源。


总结:看懂底层,才能防住高层

通过这次完整的跟踪实验,你应该已经建立起一种直觉:

内存注入不是神秘的技术,而是一系列可观察、可拦截、可分析的行为序列。

而 OllyDbg 正是帮你把这些抽象概念具象化的最佳工具。它让你看到:

  • 内存是如何被悄悄分配的;
  • 数据是如何变成代码的;
  • 控制流是如何被悄然转移的。

这些技能不仅适用于分析 PoC 级别的注入器,更是你在面对 APT 攻击、无文件恶意软件、反射式 DLL 注入等复杂威胁时,进行深度狩猎的基础。

下次当你听说某款 EDR 检测到了“异常线程创建”,不妨想想:它是怎么知道那个地址不该被执行的?答案,往往就藏在CreateRemoteThread的参数里,在PAGE_EXECUTE_READWRITE的标记里,在那一片不该存在的 RWX 内存里。

而你要做的,就是学会去看。

如果你也曾在调试器中见过 EIP 跳进一片灰色内存区而心头一紧,欢迎在评论区分享你的“破案”经历。

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

Delphi反编译终极指南:IDR快速上手与高效分析技巧

Delphi反编译终极指南&#xff1a;IDR快速上手与高效分析技巧 【免费下载链接】IDR Interactive Delphi Reconstructor 项目地址: https://gitcode.com/gh_mirrors/id/IDR IDR&#xff08;Interactive Delphi Reconstructor&#xff09;是一款专为Delphi语言设计的强大反…

作者头像 李华
网站建设 2026/1/21 21:38:54

JADX反编译工具完整使用指南:从基础到实战

JADX反编译工具完整使用指南&#xff1a;从基础到实战 【免费下载链接】jadx skylot/jadx: 是一个用于反编译Android应用的工具。适合用于需要分析和学习Android应用实现细节的开发者。特点是可以提供反编译功能&#xff0c;将Android应用打包的APK文件转换成可阅读的Java代码。…

作者头像 李华
网站建设 2026/1/22 14:45:12

yolov5作者推荐的图像预处理技巧:配合DDColor提升数据质量

YOLOv5作者推荐的图像预处理技巧&#xff1a;配合DDColor提升数据质量 在目标检测任务中&#xff0c;我们常常默认训练数据是“足够好”的——清晰、标注准确、色彩自然。但现实往往没那么理想&#xff0c;尤其是当项目涉及历史影像、老旧监控视频或低质量扫描件时&#xff0c…

作者头像 李华
网站建设 2026/1/22 15:13:25

VS Code JSON插件高效使用指南:轻松管理配置文件

VS Code JSON插件高效使用指南&#xff1a;轻松管理配置文件 【免费下载链接】vscode-json Json for Visual Studio Code 项目地址: https://gitcode.com/gh_mirrors/vs/vscode-json 还在为复杂的JSON配置文件而头疼吗&#xff1f;每次打开package.json或tsconfig.json时…

作者头像 李华
网站建设 2026/1/22 13:06:30

DroidRun智能自动化:重新定义移动设备交互新范式

DroidRun智能自动化&#xff1a;重新定义移动设备交互新范式 【免费下载链接】droidrun 用自然语言命令自动化Android设备交互&#xff0c;支持多LLM提供商 项目地址: https://gitcode.com/gh_mirrors/dr/droidrun 在数字化浪潮席卷各行各业的今天&#xff0c;手动操作移…

作者头像 李华