DLL注入(英语:DLL injection)是一种计算机编程技术,它能够强行使另外一个进程加载一个动态连接库以在其地址空间内运行指定代码[1]。在Windows操做系统上,每一个进程都有独立的进程空间,即一个进程是没法直接操做另外一个进程的数据的(事实上,不只Windows,许多操做系统也是如此)。可是DLL注入是用一种不直接的方式,来实现操做其余进程的数据。假设咱们有一个DLL文件,里面有操做目标进程数据的程序代码逻辑,DLL注入就是使目标进程加载这个DLL,加载后,这个DLL就成为目标进程的一部分,目标进程的数据也就能够直接操做了。
git
本文编写的代码所作的工做至关于上图中注入器所作的工做,所说起的DLL均为C/C++语言生成的DLL。github
(1) 打开目标进程
(2) 在目标进程开辟一段内存空间
(3) 往开辟的内存空间中写入要注入的DLL的路径
(4) 给目标建立一个线程, 加载DLL编程
Windows下有个名为OpenProcess的函数能够打开一个进程,它的原型以下:安全
HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId );
返回值:若是打开成功,返回一个进程句柄,不然返回NULL函数
代码实现以下:this
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid); if (NULL == hProcess) { OutputDebugString("Cannot open this process.\n"); return -1; }
这段代码根据进程的pid以PROCESS_ALL_ACCESS权限来打开一个进程,并返回进程句柄,进程pid能够经过如下方式得到:操作系统
int GetPidByProcessName(const char* ProcessName) { HANDLE Processes = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL); PROCESSENTRY32 ProcessInfo = { 0 }; ProcessInfo.dwSize = sizeof(PROCESSENTRY32); while (Process32Next(Processes, &ProcessInfo)) { if (strcmp(ProcessInfo.szExeFile, ProcessName) == 0) { return ProcessInfo.th32ProcessID; } } return -1; }
该函数经过进程名返回它对应的进程pid线程
VirtualAllocEx函数能够在目标进程申请一块内存空间,函数原型以下:指针
LPVOID VirtualAllocEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect );
返回值: 若是内存申请成功,返回内存空间的首地址,不然返回NULLcode
代码实现以下:
LPVOID lpAddr = VirtualAllocEx(hProcess, NULL, strlen(DllPath), MEM_COMMIT, PAGE_READWRITE); if (NULL == lpAddr) { OutputDebugString("Cannot alloc memory.\n"); return -1; }
这段代码是在目标进程申请开辟一块内存空间,申请开辟的内存空间大小是DLL完整路径所占用的字节数,申请成功将会返回内存空间的起始地址
WriteProcessMemory能够向指定的内存地址中写入数据,函数原型以下:
BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten );
返回值: 返回一个非零值表明写入成功,返回零则写入失败
代码实现以下:
BOOL isOk = WriteProcessMemory(hProcess , lpAddr, DllPath, strlen(DllPath), NULL); if (!isOk) { OutputDebugString("Cannot write memory.\n"); return -1; }
这段代码是将DLL的完整路径写入到上一步开辟的内存空间中
(1)GetModuleHandle函数根据模块名称获得模块的句柄,原型以下:
HMODULE GetModuleHandleA( LPCSTR lpModuleName );
返回值:指定模块的句柄
(2)GetProcAddress函数能够根据函数名来获得模块中的一个导出函数的地址,原型以下:
FARPROC GetProcAddress( HMODULE hModule, LPCSTR lpProcName );
返回值:相应的导出函数的地址
(3)CreateRemoteThread用于在指定进程的虚拟空间中开启一个线程,原型以下:
HANDLE CreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
返回值:建立成功返回一个线程句柄,不然返回一个NULL
(4)LoadLibraryA是LoadLibrary函数的ASCII码版本,它的函数原型以下:
HMODULE LoadLibraryA( LPCSTR lpLibFileName );
返回值:加载成功会返回模块的句柄,加载失败返回NULL
代码实现以下:
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 );
上面的代码使用GetProcAddress函数得到kernel32.dll模块中LoadLibraryA函数的地址,而后在目标进程开启一个线程调用LoadLibraryA函数。lpAddr被写入DLL的完整路径,把它传入CreateRemoteThread函数,至关于就是把DLL的完整路径传给LoadLibraryA函数。
DLL注入了目标进程后,若是想要把它从目标进程卸载,须要进行如下步骤:
(1) 打开目标进程
(2) 给目标建立一个线程, 卸载DLL
打开目标进程的操做和注入同样,再也不详细展开,代码以下:
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid); if (NULL == hProcess) { OutputDebugString("Cannot open this process.\n"); return -1; }
这一步代码实现和注入的最后一步大致同样,一样是开启线程调用一个函数,但这个在线程里执行的函数是FreeLibrary。
FreeLibrary函数原型以下:
BOOL FreeLibrary( HMODULE hLibModule );
返回值:成功会返回一个非0值
代码实现以下:
HMODULE hKernel32Module = GetModuleHandle("kernel32.dll"); if (NULL == hKernel32Module) { OutputDebugString("Cannot find kernel32.dll.\n"); return -1; } FARPROC hFarProc = GetProcAddress(hKernel32Module, "FreeLibrary"); if (NULL == hFarProc) { OutputDebugString("Cannot get function address.\n"); return -1; } HMODULE hModule = GetModuleHandleByName(ModuleName,pid); if (NULL == hModule) { OutputDebugString("Cannot find this module.\n"); return -1; } HANDLE hThread = CreateRemoteThread(hProcess ,NULL , 0 , (LPTHREAD_START_ROUTINE)hFarProc , hModule , 0 , NULL );
因为FreeLibrary函数须要传入一个模块的句柄,那么咱们须要从目标进程中扫描并找到咱们想要卸载的模块,而后返回它的句柄:
HMODULE GetModuleHandleByName(const char* ModuleName,DWORD pid) { HANDLE Processes = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); MODULEENTRY32 ModuleInfo = { 0 }; ModuleInfo.dwSize = sizeof(MODULEENTRY32); char buf[0x100]; while (Module32Next(Processes, &ModuleInfo)) { if (strcmp(ModuleInfo.szModule, ModuleName) == 0) { return ModuleInfo.hModule; } } return NULL; }