DLL注入(英语:DLL injection)是一种计算机编程技术,它可以强行使另一个进程加载一个动态链接库以在其地址空间内运行指定代码,在Windows操作系统上,每个进程都有独立的进程空间,即一个进程是无法直接操作另一个进程的数据的(事实上,不仅Windows,许多操作系统也是如此)。但是DLL注入是用一种不直接的方式,来实现操作其他进程的数据。假设我们有一个DLL文件,里面有操作目标进程数据的程序代码逻辑,DLL注入就是使目标进程加载这个DLL,加载后,这个DLL就成为目标进程的一部分,目标进程的数据也就可以直接操作了

DLL注入基本流程

(1) 打开目标进程

1
2
3
4
5
6
7
8
HANDLE OpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwProcessId
);
//- dwDesiredAccess 访问权限
//- bInheritHandle 是否继承句柄
//- dwProcessId 要打开的进程pid
1
2
3
4
5
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
if (NULL == hProcess) {
OutputDebugString("Cannot open this process.\n");
return -1;
}

(2) 在目标进程开辟一段内存空间

1
2
3
4
5
6
7
LPVOID VirtualAllocEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
  • hProcess 要向其申请内存空间的进程
  • lpAddress 申请的内存所在的地址,传入NULL函数帮我们决定地址
  • dwSize 申请的内存空间的大小,单位为字节
  • flAllocationType 要申请的内存空间类型
  • flProtect 内存保护常量
1
2
3
4
5
LPVOID lpAddr = VirtualAllocEx(hProcess, NULL, strlen(DllPath), MEM_COMMIT, PAGE_READWRITE);
if (NULL == lpAddr) {
OutputDebugString("Cannot alloc memory.\n");
return -1;
}

(3) 往开辟的内存空间中写入要注入的DLL的路径

1
2
3
4
5
6
7
8
BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesWritten
);

  • hProcess 要写入的进程的句柄
  • lpBaseAddress 要写入的目标地址
  • lpBuffer 要写入的数据
  • nSize 要写入的数据的大小
  • lpNumberOfBytesWritten 一个用于接收传入目标进程的字节数的指针变量
  • 返回值: 返回一个非零值代表写入成功,返回零则写入失败
1
2
3
4
5
BOOL isOk = WriteProcessMemory(hProcess , lpAddr, DllPath, strlen(DllPath), NULL);
if (!isOk) {
OutputDebugString("Cannot write memory.\n");
return -1;
}

(4) 给目标创建一个线程, 加载DLL

GetModuleHandle函数根据模块名称得到模块的句柄
返回值:指定模块的句柄
GetProcAddress函数可以根据函数名来得到模块中的一个导出函数的地址
CreateRemoteThread用于在指定进程的虚拟空间中开启一个线程

1
2
3
4
5
6
7
8
9
10
HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);

  • Process 目标进程句柄
  • lpThreadAttributes 线程安全属性,传入NULL时使用默认属性
  • dwStackSize 线程初始栈大小,传入0使用默认栈大小
  • lpStartAddress 线程中要执行的函数的地址
  • lpParameter 传入线程函数的参数
  • dwCreationFlags 创建线程的参数,传入0时线程立即执行
  • lpThreadId 接收线程标识的指针,传入NULL时线程不返回标识

返回值:创建成功返回一个线程句柄,否则返回一个NULL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
HMODULE hKernel32Module = GetModuleHandle("kernel32.dll");
if (NULL == hKernel32Module) {
OutputDebugString("Cannot find kernel32.dll.\n");
return -1;
}
FARPROC hFarProc = GetProcAddress(hKernel32Module, "LoadLibraryA");
if (NULL == hFarProc) {
OutputDebugString("Cannot get function address.\n");
return -1;
}
HANDLE hThread = CreateRemoteThread(hProcess
, NULL
, 0
, (LPTHREAD_START_ROUTINE)hFarProc
, lpAddr
, 0
, NULL
);