一、注入技术的用途

Windows内存体系(1)--虚拟地址空间文章中我们可以知道:

在 Windows 系统中,每个进程都有自己私有的地址空间。当我们用指针来引用内存时,指针的值表示的是进程自己的地址空间的一个虚拟的内存地址。进程不能通过指针来引用其他进程地址空间的内存。 这种设计提升了操作系统的健壮性和安全性,比如一个进程存在缺陷,可能会引用和覆盖随机地址处的内存数据,那么我们就不用担心这个缺陷会影响到操作系统及其他进程。

独立的地址空间有利于系统的稳定性。但也带来了诸多不便,下面列举了需要跨越进程的边界来访问另一个进程地址空间的情况:

  • 我们要从另一个进程创建的窗口来派生子类窗口,比如附着在 Windows 资源管理器上的一些小插件等。
  • 我们需要假借其他进程之名做某些事情。
  • 我们需要获取其他进程的更多详细信息,如加载了哪些 dll 等。
  • 我们需要对其他进程的某些函数进行HOOK。
  • 以及干一些羞羞的事情…

为了满足上述需求,我们可以使用 DLL 注入的技术,将我们自己开发的 DLL 注入到另一个进程的地址空间中,这样 DLL 中的代码就可以在该进程地址空间中执行。

二、什么样的 DLL 可以被注入

理论上任何 DLL 都可以被注入到其他进程之中,但是大多数情况下,我们注入到其他进程之中是为了实现某些功能、做某些事情的,所以我们需要在 DLL 被注入之后,DLL 中的功能代码能够被自动执行。

DLL 被首次载入到进程中时,会收到DLL_PROCESS_ATTACH通知,即调用DllMain函数,并且参数fdwReason的值被设为DLL_PROCESS_ATTACH

我们可以在收到DLL_PROCESS_ATTACH通知时开始我们的业务逻辑。

下面是一个最简单的 dll 的源码,在被注入成功后(即收到DLL_PROCESS_ATTACH通知时)弹出消息提示框:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
BOOL APIENTRY DllMain(HMODULE hModule, DWORD fdwReason, LPVOID lpReserved ) {
switch(fdwReason) {
case DLL_PROCESS_ATTACH:
{
MessageBox(NULL, TEXT("我已经被注入啦"), TEXT("信息"), MB_ICONINFORMATION);
break;
}
case DLL_THREAD_ATTACH:
{
break;
}
case DLL_THREAD_DETACH:
{
break;
}
case DLL_PROCESS_DETACH:
{
break;
}
}
return TRUE;
}

另外,当 DLL 被从目标进程卸载时,DLL 会收到DLL_PROCESS_DETACH通知,我们可以在该通知的处理过程中做最后的善后工作,防止出现资源泄漏、程序崩溃等问题。