之前呢细学习了一遍 dll 注入的流程,然而对于 dll 怎么注入进去还是一无所知,所以今天就刚好来学习一下。

动态链接库

编写dll

我们说 exe 和 dll 文件都是 PE 文件,一般 exe 只有导入表,dll 只有导出表。导入表会显示你导入了哪些 dll ,dll 中导入了哪些函数等等。dll 的导出表则会显示你这里有多少可以被导入的函数。

新建链接库,创建 h 文件,输入申明

1
extern "C" __declspec(dllexport) int add(int num1,int num2);

主要是因为 C++ 有名称粉碎机制,因为 具有多态特性的C++支持函数的重载,函数不再以函数名称作为唯一标识。只要满足构成重载的条件,两个(或多个)功能不同的函数可以有相同的函数名称。这样一来,函数的调用者会获得多态性带来的极大方便(虽然函数的编写者的工作量没有改变,所有的同名函数仍需要一个一个地去编写)。构成函数重载的条件是:

  1. 作用域相同
  2. 函数名称相同
  3. 参数不同(类型,个数,顺序)(另外:返回值类型、调用约定类型并不作为参考)

为了支持函数重载这一新特性,编译器的开发者们大多选择使用名称粉碎机制,即把函数的原有名称和参数类型、个数、顺序等信息融合成一个新的函数名称。这个新的名称就是此函数的唯一标识。

因此如果不导出成 C,则我们无法通过 add 函数名调用。

创建一个 cpp 文件简单写一下实现,生成 dll,拖入 LordPE 就能看到导出函数。

动态调用dll

讲两个 API:

LoadLibraryA

加载指定 DLL 并返回模块句柄,参数为字符串,就是 dll 的路径。

GetProcAddress

获取指定 dll 的导出函数的地址。

第一个参数是模块句柄,第二个参数是模块函数,返回值为函数的地址。

通过这两个函数,我们可以拿到所有函数的地址,然后就能进行调用。

1
2
3
4
5
6
7
8
#include<stdio.h>
#include<windows.h>
typedef int (*f)(int, int);
int main() {
HMODULE hModule = LoadLibrary(L"Selfdll.dll");
f p = (f)GetProcAddress(hModule, "add");
printf("%d",(*p)(1, 2));
}

运行结果

静态加载dll

只需要 dll 和对应的头文件,然后 include 就好了。

远程线程注入

首先介绍一个 API:

CreateThread

里面几乎只有一个参数,那就是线程回调函数,然后当然还有返回地址,返回线程 id 啥的,这里我们都可以不用管,几乎是与 Linux 的创建线程函数一致。

还有一个远程版本的叫 CreateRemoteThread,它可以给别的进程创建一个线程并可以在本进程创建那个进程调用的回调函数。我们可以在回调函数中加载指定的 dll,在 dllmain 的入口当中,有一个 switch 的四个选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

我们可以在 DLL_PROCESS_ATTACH 的选项中加入代码,让它在加载的时候调用执行。

那么我们的步骤是:

  1. 打开指定进程获得句柄
  2. 开辟远程进程的空间,分配可读可写段。
  3. 调用 WriteProcessMemory 将 dll 路径写入该内存区域。
  4. 创建远程线程,回调函数使用 LoadLibrary 加载指定 dll。
  5. 等待返回
  6. 释放空间
  7. 释放句柄
  8. 返回结果

远程线程注入demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void InjectModule(TCHAR* szPath)
{
//打开指定进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId);
//分配可读可写内存
LPVOID lpAddress=VirtualAllocEx(hProcess,NULL,0x100,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
size_t dwWriteLength = 0;
//往该内存写入模块路径
WriteProcessMemory(hProcess, lpAddress, szPath, (wcslen(szPath)+1)*2,&dwWriteLength);
//创建远程线程,并使用回调函数去加载dll
HANDLE hThread = CreateRemoteThread(hProcess,NULL,NULL,(LPTHREAD_START_ROUTINE)LoadLibraryW,lpAddress,NULL,NULL);
//永久等待线程创建完毕
WaitForSingleObject(hThread,-1);

VirtualFreeEx(hProcess,lpAddress,0,MEM_RELEASE);
CloseHandle(hProcess);
CloseHandle(hThread);
AfxMessageBox(L"完成");
}