这次使用远程线程来进行DLL注入,它提供了最高的灵活性。从根本上说DLL注入要求目标进程中的一个线程使用LoadLibrary来载入我们的DLL。
由于我们不能随意的控制别的线程,因此我们需要在目标进程创建一个新的线程,远程创建线程可以使用 CreateRemoteThread 来进行创建,下面是函数原型。
HANDLE CreateRemoteThread(
[in] HANDLE hProcess,
[in] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] SIZE_T dwStackSize,
[in] LPTHREAD_START_ROUTINE lpStartAddress,
[in] LPVOID lpParameter,
[in] DWORD dwCreationFlags,
[out] LPDWORD lpThreadId );
除了CreateRemoteThread有一个额外的参数 hProcess 以外其他的都和CreateThread完全相同,hProcess 这个参数表示创建的进程归那个进程所有。
下一步我们需要让该线程调用LoadLibrary函数来载入我们的DLL,通过函数原型发现他们两个非常相似,都是一个参数一个返回值,函数约定都是WINAPI 。
LoadLibrary函数原型
HMODULE WINAPI LoadLibraryA(_In_ LPCSTR lpLibFileName);
HMODULE WINAPI LoadLibraryW(_In_ LPCWSTR lpLibFileName);
线程回调的原型
DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter);
那么只要我们把线程函数回调地址改成LoadLibrary是不是就ok了?如下
使用多字符
HANDLE hThread = CreateRemoteThread(handle, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, "c:\\myLib.dll", 0, NULL);
使用宽字符
HANDLE hThread = CreateRemoteThread(handle, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, L"c:\\myLib.dll", 0, NULL);
当新线程被创建时会立刻调用LoadLibrary,并传入DLL路径,但是这还存在其他两个问题。
第一 直接把LoadLibrary传给CreateRemoteThread第四个参数,在编译和链接的时候会把函数放到一个二进制文件的导入段中,通过这个二进制的文件来获取实际的跳转地址。当我们在远程直接调用LoadLibrary,会被解析成我们模块导入段的LoadLibrary,所以解析的数据鬼知道是那个地址,这样会造成违规访问。
第二个问题就是 你传入给LoadLibrary的字符串,也就是CreateRemoteThread第五个参数,你传入的是本地空间的字符串指针,所以当LoadLibrary进行操作字符串时也会发生访问违规。
解决第一个问题我们可以使用GetProcAddress函数来获取LoadLibrary的实际地址,因为LoadLibrary函数是在Kernel32.dll里面,系统会把Kernel32.dll映射到每一个进程里,所以以下操作可以获取LoadLibrary在当前线程的真实地址。
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleW(L"Kernel32"), "LoadLibraryW");
HANDLE hThread = CreateRemoteThread(handle, NULL, 0, pfnThreadRtn, L"c:\\myLib.dll", 0, NULL);
好了,第一个问题解决了还有第二个问题,怎么把字符串放到远程的线程中呢?好在windows为我们提供了在一个进程给另一个进程的地址空间分配一块内存 VirtualAllocEx ,还提供了让我们释放这块内存 VirtualFreeEx。
我们在另一个进程开辟了内存,还需要把字符串复制到开辟的内存中,所以需要WriteProcessMemory函数来把当前进程的字符串写入到另一个进程中。
wstring dllPath = L"c:\\myLib.dll";
DWORD dwSize = dllPath.length();
dwSize = dwSize * sizeof(WCHAR);
LPWCH pszLibFileRemote = (LPWCH)VirtualAllocEx(handle, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
// 把dll路径复制到创建的内存中
WriteProcessMemory(handle, pszLibFileRemote, dllPath.c_str(), dwSize, NULL);
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleW(L"Kernel32"), "LoadLibraryW");
HANDLE hThread = CreateRemoteThread(handle, NULL, 0, pfnThreadRtn, "c:\\myLib.dll", 0, NULL);
这个样子就基本完成了。下面补充一下还需要进行获取要注入进程的句柄,可以通过进程id来获取,下面是完整的一个注入函数。
//参数dwProcessId 表示要注入的进程的PID
//参数dllPath 表示需要注入进程的路径
BOOL Injection(DWORD dwProcessId, wstring dllPath)
{
HANDLE handle = OpenProcess(
PROCESS_QUERY_INFORMATION | // 需要检索有关进程的某些信息
PROCESS_CREATE_THREAD | // 需要创建线程。
PROCESS_VM_OPERATION | // 需要对进程的地址空间执行操作
PROCESS_VM_WRITE, // 需要使用WriteProcessMemory 写入进程中的内存
FALSE, dwProcessId);
if (handle == NULL)
{
return FALSE;
}
DWORD dwSize = dllPath.length();
dwSize = dwSize * sizeof(WCHAR);
LPWCH pszLibFileRemote = (LPWCH)VirtualAllocEx(handle, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (NULL == pszLibFileRemote)
{
return FALSE;
}
// 把dll路径复制到创建的内存中
if (!WriteProcessMemory(handle, pszLibFileRemote, dllPath.c_str(), dwSize, NULL))
{
return FALSE;
}
// 获取LoadLibrary所在Kernel32.dll中的实际地址(系统每个进程都会需要Kernel32.dll)
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleW(L"Kernel32"), "LoadLibraryW");
HANDLE hThread = CreateRemoteThread(handle, NULL, 0, pfnThreadRtn, pszLibFileRemote, 0, NULL);
if (hThread == NULL)
{
return FALSE;
}
if (pszLibFileRemote != NULL)
VirtualFreeEx(handle, pszLibFileRemote, 0, MEM_RELEASE);
if (hThread != NULL)
CloseHandle(hThread);
if (handle != NULL)
CloseHandle(handle);
return TRUE;
}
下面是卸载这个dll
BOOL UnInjection(DWORD processId, wstring dllPath)
{
BOOL bRtn = FALSE;
HANDLE hThread;
HANDLE hProcess;
HANDLE hthSnapshot;
//远程删除操作步骤
do
{
// 获取指定进程的快照,以及这些进程使用的堆、模块和线程。
hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);
if (hthSnapshot == INVALID_HANDLE_VALUE) break;
// 获取模块信息
MODULEENTRY32W me = { sizeof(me) };
BOOL bFound = FALSE;
//检索模块的信息
BOOL bMoreMods = Module32FirstW(hthSnapshot, &me);
for (; bMoreMods; bMoreMods = Module32NextW(hthSnapshot, &me))
{
//判断是否是我们加载的dll
bFound = (_wcsicmp(me.szModule, dllPath.c_str()) == 0) || (_wcsicmp(me.szExePath, dllPath.c_str()) == 0);
if (bFound) break;
}
if (!bFound) break;
hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION | // Required by Alpha
PROCESS_CREATE_THREAD | // For CreateRemoteThread
PROCESS_VM_OPERATION | // For VirtualAllocEx/VirtualFreeEx
PROCESS_VM_WRITE, // For WriteProcessMemory
FALSE, processId);
if (hProcess == NULL)
break;
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleW(L"Kernel32"), "FreeLibrary");
// 在远程进程中创建另一个线程,调用FreeLibrary
hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, me.modBaseAddr, 0, NULL);
if (hThread == NULL)
break;
WaitForSingleObject(hThread, INFINITE);
bRtn = TRUE;
} while (FALSE);
if (hProcess != NULL)
{
CloseHandle(hProcess);
}
if (hThread != NULL)
{
CloseHandle(hThread);
}
if (hthSnapshot != NULL)
{
CloseHandle(hthSnapshot);
}
return bRtn;
}
参考资料:windows核心编程第五版