前排提醒:本文纯技术分享,不含任何样本,请放心阅读。如有雷同,那你电脑可能也有问题(笑)。
一、我是如何中马并发觉自己中马的?
昨天晚上闲着没事,开System Informer看了一下进程,列表里赫然躺着一个colorcpl.exe。
等会,colorcpl.exe?
???
打开Ark一看……好好好,反射加载dll。
掏出 Process Explorer 一看,好家伙,一个我不认识的日历进程YXCalendar.exe内存映射里躺着一个熟悉的陌生人:libcurl.dll。
按理说 libcurl 出现倒也不奇怪,毕竟很多程序都要用。但问题是,这文件尺寸比正常的大了那么一丢丢……就一丢丢。多出来的那部分,不用想也知道是"房东加的隔断"。
而且,libcurl是白加黑重灾区。
更离谱的是,这东西在我电脑里不知道待了多久。可能是我装某破解软件的时候顺手带进来的,也可能是我下载了什么带"学习资料"字样的压缩包……算了不重要,先抓出来看看。
二、初见样本:一场典型的"白函数"噩梦
拖进 IDA,第一眼差点没被送走。
导出表翻开一看,6000+ 个导出函数,绝大多数都是 ??_7wxDialog 这种 C++ 模板/虚表符号。往下拉、往下拉、再往下拉……满屏都是这些白函数的 vftable 和 RTTI 信息,真正的恶意逻辑不知道藏在哪里。
这感觉就像你在一座垃圾山里找一颗钻石,但钻石外面包满了卫生纸。
我按经验去看了 OEP,标准的 CRT 初始化流程,干干净净。然后又用 strings 扫了一遍 .rdata——除了常规的 kernel32、msvcrt 这些,几乎没看到什么可疑字符串。倒是扫到了 libcurlOrg.dll,确认是 DLL 代{过}{滤}理劫持无疑,但恶意代码到底在哪?
curl_easy_cleanup → libcurlOrg.curl_easy_cleanup
curl_easy_duphandle → libcurlOrg.curl_easy_duphandle
curl_easy_escape → libcurlOrg.curl_easy_escape
curl_easy_getinfo → libcurlOrg.curl_easy_getinfo
curl_easy_init → libcurlOrg.curl_easy_init
curl_easy_perform → libcurlOrg.curl_easy_perform
curl_easy_recv → libcurlOrg.curl_easy_recv
curl_easy_send → libcurlOrg.curl_easy_send
curl_easy_setopt → libcurlOrg.curl_easy_setopt
(全是转发的函数调用……)
我手动翻了半天,越看越迷糊。那些白函数调用之间偶尔夹杂几个莫名其妙的 jmp,看着像是会跳转到某个非导出区段,但 IDA 的交叉引用根本连不上——因为压根就没引用,全是运行时拼出来的。
到这一步我已经有点烦躁了。这要是硬刚手工逆向,估计得通宵。
于是我一拍大腿:让 AI 来。
二、AI Agent 介入:自动化分析引擎
我之前的逆向工作流里有个自己写的 Python + Capstone 小脚本,本质上就是一个简单的递归反汇编器:从 OEP 出发,遇到 call 就递归,把指令打印出来。之前对付过几个小样本,还算顺手。
但这次的样本真正的恶意逻辑根本不经过 OEP,而是藏在某个导出函数里,再加上大量 call dword ptr [0xNNNN],没有符号,没有注释,看汇编等于看天书
于是我和 AI 商量了一下,决定给这个脚本做个升级:
3.1 改造一:支持从任意 RVA 出发递归反汇编
原来的脚本只能从 OEP 出发,这次加上 --rva 参数,可以从任何地址开始递归。这样我就能直接从 curl_global_init(RVA=0x0010F240)出发,不再被 OEP 那堆 CRT 垃圾淹没。
3.2 改造二:IAT 解析
这是最爽的一个改动。脚本读取 PE 的导入表,把 call dword ptr [0xNNNN] 自动解析成 call KERNEL32!CreateProcessW 这种人类可读的格式。
效果立竿见影——原本满屏的 call dword ptr [0x1010B2C0],现在一眼就能看出来这是 CreateProcessW、LoadLibraryA、GetProcAddress……恶意意图瞬间暴露。
跑完之后我拿到了 50 个函数、4000 多条指令的报告,其中带着两个 [PROC-INJ] 和一个 [DYN-API] 标注——
跟着AI的分析,我迅速在IDA定位到了关键载荷的加载逻辑:
(upload://6K75HA6mDdsQBxEQHHUO3NSxPa6.png)
(检测cpuid用于反调试)
![PixPin_2026-05-06_13-20-04|225x500]
(经典Process Hollowing)
(字符串拼接出目标进程和shellcode路径)
————
这个函数做了三件事:
SSE 拼接目标进程路径:用 movups 把四个 xmmword 常量拼到栈上,最终合成 C:\Windows\SysWOW64\colorcpl.exe。对,就是我一开始发现异常的那个进程。
这里要吐槽一下攻击者的心机——正常程序直接在 .rdata 里放字符串,strings 一扫就出来。他偏要把字符串拆成 4 块 128 位常量,用 SSE 指令运行时拼。strings 工具完全失效,IDA 里看起来也只是几个 xmmword 数值(当然F5还是能够直接拼好的),不仔细分析根本不知道这是啥。
读取配置文件:用 C++ I/O 流打开 C:\Program Files (x86)\MCWinMailAll\dkcinst.ini——这个路径也是 IDA 直接识别出来的
调用注入函数:把文件数据、大小、目标进程路径传给 sub_1010D6A0。
4.3 进程镂空:sub_1010D6A0
这才是重头戏。完整流程就是教科书级的 Process Hollowing:
CreateProcessW(“colorcpl.exe”, CREATE_SUSPENDED | CREATE_NO_WINDOW)
挂起创建,窗口隐藏
LoadLibraryA(“kernel32.dll”) + 5 次 GetProcAddress
动态解析注入 API 组合:VirtualAllocEx、WriteProcessMemory、GetThreadContext、SetThreadContext、ResumeThread
VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
在目标进程里分配 RWX 内存(老演员了)
WriteProcessMemory(hProcess, remoteAddr, payload, size, NULL)
把 Payload 写进去
GetThreadContext + SetThreadContext
修改线程上下文,把 EIP 重定向到注入代码
ResumeThread(hThread)
恢复执行,原进程被"偷梁换柱"
至此,分析结束。
三、结语
这次经历给我的最大感受是:现代恶意样本的伪装水平越来越高,但分析工具的自动化水平也在同步提升。
以前遇到这种"白函数淹没"的样本,可能要花几天时间手工扫导出表、翻字符串、追调用链。现在借助 AI Agent,我可以在几个小时内:定位恶意入口 → 递归反汇编 → 提取关键字符串 → 还原完整攻击链路。
当然,前提是你要知道自己在问什么。AI 不会替你思考,但它能极大地降低你思考到结论之间的执行成本。
最后,忠告大家:
- 别在实体机里跑来历不明的程序(我当然知道你们做不到,但还是要说)
- 保持 Process Explorer / System Informer 的习惯,没事看看进程列表
- AI 是个不错的副手,尤其是在你被 6000 个白函数淹没的时候。
帖子写于某次深夜分析后的恍惚状态,如有错别字请见谅。 IDA 永远的神,但 Python + Capstone + AI Agent 在某些场景下是真的香。
3 个帖子 - 3 位参与者