Dll注入与卸载入门学习总结
本文所有代码均在该链接:注入Dll示例VS项目
动态链接库(Dll)入门知识:微软教程
获取进程标识符(PID)
方法一:根据进程名称获取进程标识符(PID)
通过遍历系统中所有进程的快照来获取,重要的API如下:
- CreateToolhelp32Snapshot(创建指定进程及其相关堆、模块、线程信息的快照)
- PROCESSENTRY32(用来存放快照进程信息的一个结构体)
- Process32First(检索快照中第一个进程的信息)
- Process32Next(检索快照中下一个进程的信息)
示例代码:
//根据进程名称获取操作系统中该进程的标识符(PID)
DWORD getProcIDByName(const wchar_t* lpProcName)
{
//获取当前系统中所有进程的快照句柄
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
//判断获取是否成功
if (hSnapShot == INVALID_HANDLE_VALUE)
{
return -1;
}
//结构体实例化,PROCESSENTRY32是用来存放快照进程信息的一个结构体
PROCESSENTRY32 pe = { sizeof(pe) };
//遍历快照并将进程信息存入指定的PROCESSENTRY32结构体
for (BOOL ret = Process32First(hSnapShot, &pe); ret; ret = Process32Next(hSnapShot, &pe))
{
//比较进程的可执行文件名称
if (wcscmp(pe.szExeFile, lpProcName) == 0)
{
//关闭快照句柄
CloseHandle(hSnapShot);
//返回查找到的进程标识符
return pe.th32ProcessID;
}
}
CloseHandle(hSnapShot);
//没有指定名称的进程则返回0
return 0;
}
方法二:根据窗口名称获取创建该窗口的进程的标识符(PID)
通过窗口名称来查找创建该窗口的进程的标识符,重要的API如下:
- FindWindowA(检索指定类名、窗口名的顶级窗口句柄)
- GetWindowThreadProcessId(获取指定窗口线程的标识符,同时也能获取创建该窗口的进程的标识符,注意:这里我们不需要该函数的返回值而应该将第二个参数指向接收进程标识符的变量)
示例代码:
DWORD dwProcID;
HWND hwnd = FindWindowA(NULL, "调用Dll");
if (hwnd == NULL)
{
MessageBoxA(NULL, "未发现\"调用Dll\"进程", "注入提示", MB_OK);
return;
}
GetWindowThreadProcessId(hwnd, &dwProcID);
获取进程中指定模块的地址
与“根据进程名称获取进程标识符(PID)”类似,通过遍历指定进程的所有模块的快照来获取,重要的API如下:
- MODULEENTRY32(用来存放指定进程模块列表的一个结构体)
- Module32First(检索快照中第一个模块的信息)
- Module32Next(检索快照中下一个模块的信息)
示例代码:
//根据模块名称获取指定进程中该模块的地址
LPVOID getModuleAddressByName(DWORD dwProcID, const wchar_t* lpModuleName)
{
//获取当前系统中指定进程的模块快照句柄
HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcID);
//判断获取是否成功
if (hModuleSnap == INVALID_HANDLE_VALUE)
{
return (LPVOID)-1;
}
//结构体实例化,MODULEENTRY32是用来存放指定进程模块列表的一个结构体
MODULEENTRY32 me = { sizeof(me) };
//遍历快照并将模块信息存入指定的MODULEENTRY32结构体
for (BOOL ret = Module32First(hModuleSnap, &me); ret; ret = Module32Next(hModuleSnap, &me))
{
//比较模块名称
if (wcscmp(me.szModule, lpModuleName) == 0)
{
//关闭快照句柄
CloseHandle(hModuleSnap);
//返回查找到的模块地址
return me.modBaseAddr;
}
}
CloseHandle(hModuleSnap);
//没有指定名称的模块则返回0
return 0;
}
注入Dll
注入Dll的大体思路是:在宿主进程中创建一个远程线程,该线程执行kernel32.dll模块中的LoadLibraryA函数来将其他动态链接库加载到宿主进程的地址空间中。
重要的API:
- CreateRemoteThread(创建远程线程)
- GetProcAddress(获取函数或变量的起始地址)
- VirtualAllocEx(分配虚拟内存)
- WriteProcessMemory(将数据写入虚拟内存)
示例代码:
//根据进程标识符向该进程中注入指定动态链接库(Dll)
HANDLE inject(DWORD dwProcID, char* lpDllPath)
{
//获取待注入进程的句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcID);
//获取kernel32.dll的模块句柄
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
//获取LoadLibraryA函数的起始地址
LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryA");
//在待注入进程中为线程函数(LoadLibraryA)的参数(动态链接库的路径)分配虚拟内存
LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, strlen(lpDllPath) + 1, MEM_COMMIT, PAGE_READWRITE);
//将动态链接库的路径写入上一语句分配的虚拟内存
WriteProcessMemory(hProcess, lpBaseAddress, lpDllPath, strlen(lpDllPath) + 1, NULL);
//在待注入进程中创建远程线程(注入动态链接库)
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, lpBaseAddress, 0, NULL);
//等待远程线程结束,防止线程阻塞,等待时间:两秒钟
WaitForSingleObject(hRemoteThread, 2000);
CloseHandle(hRemoteThread);
CloseHandle(hProcess);
return hRemoteThread;
}
卸载Dll
与注入类似,大体思路是:在宿主进程中创建一个远程线程,该线程执行kernel32.dll模块中的FreeLibrary函数来将某模块(dll)从宿主进程的地址空间中卸载。
示例代码:
//根据进程标识符从该进程中卸载指定动态链接库(Dll)
HANDLE free(DWORD dwProcID, LPVOID lpModuleAddress)
{
//获取待卸载进程的句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcID);
//获取kernel32.dll的模块句柄
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
//获取FreeLibrary函数的起始地址
LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "FreeLibrary");
//在待卸载进程中创建远程线程(卸载动态链接库模块)
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, lpModuleAddress, 0, NULL);
//等待远程线程结束,防止线程阻塞,等待时间:两秒钟
WaitForSingleObject(hRemoteThread, 2000);
CloseHandle(hRemoteThread);
CloseHandle(hProcess);
return hRemoteThread;
}