进程和诊断工具学习笔记(8.29):ListDLLs------一眼看清进程里加载了哪些 DLL,谁在偷偷注入
- [进程和诊断工具学习笔记(8.29):ListDLLs------一眼看清进程里加载了哪些 DLL,谁在偷偷注入](#进程和诊断工具学习笔记(8.29):ListDLLs——一眼看清进程里加载了哪些 DLL,谁在偷偷注入)
-
- [1. ListDLLs 是什么?](#1. ListDLLs 是什么?)
- [2. 基本使用方法(命令直接抄)](#2. 基本使用方法(命令直接抄))
-
- [2.1 列出指定进程加载的 DLL(按进程名)](#2.1 列出指定进程加载的 DLL(按进程名))
- [2.2 指定 PID](#2.2 指定 PID)
- [2.3 列出所有进程](#2.3 列出所有进程)
- [2.4 只看加载了某个可疑模块的进程(非常好用)](#2.4 只看加载了某个可疑模块的进程(非常好用))
- [3. 输出怎么看?哪些字段是真正有价值的?](#3. 输出怎么看?哪些字段是真正有价值的?)
-
- [3.1 Path(路径)](#3.1 Path(路径))
- [3.2 Base Address / Size](#3.2 Base Address / Size)
- [3.3 进程名 + PID](#3.3 进程名 + PID)
- [4. 典型实战场景](#4. 典型实战场景)
-
- [场景 1:排查"劫持/注入"行为](#场景 1:排查“劫持/注入”行为)
- [场景 2:定位错误版本 DLL / 旧库残留](#场景 2:定位错误版本 DLL / 旧库残留)
- [场景 3:无法删除 / 无法重命名某个 DLL](#场景 3:无法删除 / 无法重命名某个 DLL)
- [场景 4:安全审计 / 反外挂 / 反注入](#场景 4:安全审计 / 反外挂 / 反注入)
- [5. ListDLLs vs Process Explorer vs Handle:我该用谁?](#5. ListDLLs vs Process Explorer vs Handle:我该用谁?)
- [6. 现场操作小抄(可以直接塞进你的应急手册)](#6. 现场操作小抄(可以直接塞进你的应急手册))
- [7. 小结 & 下一步](#7. 小结 & 下一步)
进程和诊断工具学习笔记(8.29):ListDLLs------一眼看清进程里加载了哪些 DLL,谁在偷偷注入
这一篇我们进入第 8 章后半段的另一个特别有用的排障/取证工具:ListDLLs(Sysinternals 家族成员之一)。
如果你想回答下面这些问题,它基本就是标准答案:
- "这个进程到底加载了哪些 DLL?"
- "有没有被可疑模块注入?"
- "为什么同一台机器上的两台业务服结果不一样?是不是有人用了错版本的 DLL?"
- "为什么删除不了某个 DLL 文件?是不是还有进程在占用它?"
- "升级后还是调用旧版本库?到底谁还在用旧 DLL?"
ListDLLs 很轻、很直接、不需要内核调试技能,但在安全分析、兼容性故障定位、运维应急时,它的价值极高。
本篇内容包含:
- ListDLLs 是什么
- 怎么运行 & 最常用命令
- 输出怎么看(哪些字段值得盯)
- 典型排障/取证场景
- 和其他工具(Process Explorer / Handle)怎么配合
1. ListDLLs 是什么?
ListDLLs 是 Sysinternals 出的命令行工具,用来列出某个进程当前已经加载进内存的全部模块(DLL / 驱动型模块 / 插件式扩展等)。
更通俗地讲:
它可以瞬间告诉你,"这个进程的地址空间里现在塞了哪些代码库,每个库具体来自哪个路径、哪个版本、什么时候被加载的"。
为什么这很关键?因为:
- Windows 进程并不是只运行一个 EXE。
它会LoadLibrary()一堆 DLL:系统 API、运行库、第三方 SDK、浏览器控件、杀毒注入模块、Hook 库...... - 恶意代码注入往往就是"把我自己的 DLL 塞进别人的进程",然后借宿在你这个进程里跑。ListDLLs 能把这种"住进来的人"翻出来。
- 很多诡异兼容性 Bug 是"用错了 DLL 版本",尤其是同名 DLL 被其他目录覆盖 / Shadow Copy / 旧 COM 组件/野生插件残留。一旦你把加载路径拉出来,真凶往往就很明显了。
一句话:ListDLLs 是你用来回答『这个进程里都加载了谁』的放大镜。
2. 基本使用方法(命令直接抄)
2.1 列出指定进程加载的 DLL(按进程名)
cmd
listdlls.exe notepad.exe
会显示所有名为 notepad.exe 的进程及其模块。
2.2 指定 PID
cmd
listdlls.exe -p 2336
-p 后面接进程 ID(PID)。PID 通常可以通过任务管理器、tasklist、pslist、Process Explorer、Procmon 过滤栏等方式获取。
2.3 列出所有进程
cmd
listdlls.exe
不带参数直接跑,ListDLLs 会把全机上所有活跃进程都扫一遍,把每个进程的模块清单依次打印出来。
(在生产环境建议加重定向保存结果到文件,方便检索、diff)
cmd
listdlls.exe > alldlls.txt
2.4 只看加载了某个可疑模块的进程(非常好用)
cmd
listdlls.exe somehook.dll
这条不是"列出进程→看 DLL",而是颠倒过来:"给定某个 DLL 名,查是谁加载了它"。
用途:
- 某个第三方组件占用了一个老版本 DLL,想知道到底是谁还在用。
- 杀软/外挂模块注入了某个 Hook DLL,想知道被它"上身"的进程有哪些。
- 某个 DLL 无法删除,系统说"正在被占用" → 直接搜这个 DLL 名,定位哪个进程占着它。
3. 输出怎么看?哪些字段是真正有价值的?
典型 ListDLLs 输出结构大致是这样的(示意):
text
Process: notepad.exe PID: 2336
Base Size Path
0x7ff8... 0x1f000 C:\Windows\System32\kernel32.dll
0x7ff9... 0xa3000 C:\Windows\System32\USER32.dll
0x10000.. 0x25000 C:\Program Files\VendorX\OverlayHook.dll
...
核心关注点有三个:
3.1 Path(路径)
- 这个 DLL 是从哪里被加载进来的?
- 是官方系统目录(
C:\Windows\System32)? - 是某个干净的签名版程序目录(
C:\Program Files\VendorX\...)? - 还是来自某个诡异路径(临时目录、用户下载目录、可写共享目录)?
路径异常几乎就是安全隐患预警。
3.2 Base Address / Size
- Base Address 是这个 DLL 映射到进程虚拟内存时的基址。
- Size 是它的映射大小。
对一线运维来说,这两个字段通常不需要死盯,但对开发、逆向、应急响应人员,这是判断模块是否被重定位 / 是否有内存注入痕迹的线索。
例如,一些内存注入技术会将 DLL(甚至并非合法 PE 文件)手工映射到进程的私有内存区域,而不是标准 LoadLibrary() 路径。那种情况往往看起来就"不正常"(路径奇怪 / 签名缺失 / 大小可疑)。
3.3 进程名 + PID
非常重要,尤其是做证据留存时。
你要记下哪个 PID 对应哪个进程、在哪个时刻、加载了哪些 DLL。因为之后你可能要:
- 交给开发:"你这个版本在 10:35 加载了两个同名不同路径的库,请解释为什么会发生 DLL Search Order Hijacking 风险。"
- 交给安全:"RDP-Tcp 会话下的 explorer.exe 被注入了这两个奇怪模块,路径是用户临时目录,请核查是否是外挂/窃密模块。"
4. 典型实战场景
下面给几个 ListDLLs 最有价值的使用场景,基本都是现场超高频的真实需求。
场景 1:排查"劫持/注入"行为
症状:
- 某台机器的浏览器/Office/游戏客户端/业务客户端经常崩溃、闪退、各种奇怪弹窗。
做法:
cmd
listdlls.exe chrome.exe > chrome_dlls.txt
然后在输出里找:
- 来自非系统目录的 Hook、Overlay、Inject、Monitor、Protection 之类的 DLL
- 游戏辅助、输入法扩展、截屏水印、安全特辑之类厂商的模块
如果只在问题机上能看到某个 DLL,但在"干净对照机"上看不到,那你就抓到突破口了。
这就是**"DLL 差异取证"**,极其好用。
场景 2:定位错误版本 DLL / 旧库残留
很常见的一句话投诉:
"我们刚更新了 A 库的 v4.2,但应用还是在用旧版本 v4.1,所以新功能还是报错。"
ListDLLs 就是证据生成器。
-
在现场机器跑:
cmdlistdlls.exe appservice.exe > after_upgrade.txt -
看
C:\Program Files\AppVendor\libA.dll实际加载的版本是不是还在旧目录里,比如居然是:- 从
C:\app_backup_old\libA.dll被加载,而不是新目录 - 或者从一个共享路径 / 临时目录加载
- 从
这类情况经常是:
- PATH 顺序污染
- DLL Search Order 被环境变量或目录遗留影响
- 程序工作目录没变导致还在 LoadLibrary() 老路径
然后你可以非常具体地告诉研发/集成商:
"appservice.exe (PID 2336) 正在加载
C:\app_backup_old\libA.dll而不是最新的C:\Program Files\AppVendor\libA.dll。这台机器的搜索顺序被污染,导致调用了错误的实现版本。"
一句话比十张截图有用。
场景 3:无法删除 / 无法重命名某个 DLL
用户想替换某个 DLL,但资源管理器说"正在被使用,无法更改"。
你可以直接问系统:谁在占它?
cmd
listdlls.exe target.dll
ListDLLs 会反查所有进程列表,并告诉你:
"是进程 X(PID 2336) 还在加载 target.dll"。
这比瞎猜靠谱多了。
接下来你可以:
- 优雅停止那个进程/服务
- 或在维护窗口里用 Handle.exe 去强制释放(高危,后面几篇我们会讲)
场景 4:安全审计 / 反外挂 / 反注入
在安全类场景里,ListDLLs 常被当做"轻量快扫"的第一步:
- 把目标机常见高价值进程扫一遍(浏览器、shell、业务客户端、远程桌面 Session、敏感进程如 lsass.exe 不能随便附加调试,但可以观察其它用户进程)。
- 找有没有加载陌生的监控 DLL、键盘记录 DLL、截屏 DLL、流量代理 DLL。
之后再决定是否需要:
- Dump 内存
- 用 Procmon 抓行为轨迹
- 拉事件日志
- 升级为应急响应
5. ListDLLs vs Process Explorer vs Handle:我该用谁?
这三个经常一起出现,我们区分一下职责:
ListDLLs
- 输出结构化、可脚本化
- 对单个进程详细列出 DLL + 路径
- 支持"反查某 DLL 被谁加载了"
- 适合集成进批处理/现场报告
Process Explorer
- 图形界面版 + 实时刷新
- 能直接在某个进程属性对话框里看"DLLs"分页,并且还能看到数字签名是否可信
- 日常观测最舒服,但不方便批量保存差异对比
Handle
- 针对的是"句柄",即进程对系统对象的打开引用
- 不只是 DLL,它也能告诉你文件、注册表键、互斥量、命名管道等一切对象的占用情况
- 能强制关闭句柄(非常危险但在救火时很关键)
- 是调查"文件被锁""服务无法释放资源"的专用钳子
一句话总结:
- 要知道谁加载了哪个 DLL → ListDLLs
- 要知道哪个进程正占着哪个文件/互斥量 → Handle
- 要图形化看进程内部细节+签名状态→ Process Explorer
6. 现场操作小抄(可以直接塞进你的应急手册)
下面是一套非常实用的"巡检模板",适合你在问题机上直接运行,之后把结果打包给研发/安全:
cmd
:: 1. 记录当前时间 & 机器基本信息
echo === Snapshot %date% %time% === > evidence.txt
hostname >> evidence.txt
whoami >> evidence.txt
echo. >> evidence.txt
:: 2. 列出目标进程(例如 appservice.exe)的 DLL
listdlls.exe appservice.exe > dlls_appservice.txt
:: 3. 反查某个可疑模块是被谁加载的
listdlls.exe HookOverlay.dll > dlls_hookoverlay_usage.txt
:: 4. 合并快照元数据
type dlls_appservice.txt >> evidence.txt
echo. >> evidence.txt
echo --- HookOverlay usage --- >> evidence.txt
type dlls_hookoverlay_usage.txt >> evidence.txt
你交出去的 evidence.txt 里面就是:
- 机器身份
- 用户上下文
- 哪个进程加载了哪些 DLL
- 哪些进程加载了我们怀疑的 HookOverlay.dll
这个材料是能直接喂给开发、安全、第三方厂商的。
7. 小结 & 下一步
到这里你已经可以做到三件对日常运维/安全排障非常关键的事:
- 列清一个进程当前加载的所有模块(版本、路径一览无遗)。
- 识别不该出现的 DLL(排查注入、外挂、旧库残留)。
- 快速还原"为什么我删不掉这个 DLL"(因为某个进程还在用它)。
更重要的是:
你不需要现场装调试器,不需要休克系统,也不需要管理员之外的黑魔法。ListDLLs 是纯读型工具,非常适合一线排障和应急记录。
下一篇(8.30)我们会继续顺着第 8 章,把焦点从"谁加载了哪个 DLL"转向"谁还握着哪些句柄"。
也就是 Sysinternals 的另一个狠角:Handle.exe。
它能告诉你:
- 哪个进程正占着某个文件/注册表键/命名管道/同步对象
- 甚至可以在救火场景下,强制把那个句柄断开
那就是从"看"问题,升级到"直接动手处理"问题了。