从根本上来说,DLL注入技术要求目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL。由于我们不能轻易地控制别人进程中的线程,因此这种方法要求我们在目标进程中创建一个新的线程,Windows提供了创建远程线程的函数。
- 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
- );
步骤1、2、3原理简介:
字符串“D://remoteDll.dll”,位于调用进程的空间地址中,我们把这个地址传给新创建的远程线程,远程线程再把它传给LoadLibrary,当LoadLibrary去访问这个内存地址的时候,DLL的路径字符串并不在那里,远程进程的线程就很可能引发访问违规。为了解决这个问题,我们需要把DLL的字符串存放到远程进程的地址空间中去,所以会使用VirtualAllocEx和WriteProcessMemory方法。
3.1先做一个动态库remoteDll.dll
此库的功能是,一旦被注入到进程的地址空间中,就报告该进程正在使用的所有DLL,并将打印信息写入到本地文件D://out.txt中。
- const char* filepath = "D://out.txt";
-
- BOOL APIENTRY DllMain( HMODULE hModule,
- DWORD ul_reason_for_call,
- LPVOID lpReserved
- )
- {
- switch (ul_reason_for_call)
- {
- case DLL_PROCESS_ATTACH:
- {
- FILE* fp = freopen(filepath, "w", stdout);
-
- PBYTE pb = NULL;
- MEMORY_BASIC_INFORMATION mbi;
- while (VirtualQuery(pb, &mbi, sizeof(mbi)) == sizeof(mbi))
- {
- int nlen;
- char szModName[MAX_PATH];
- if (mbi.State == MEM_FREE)
- mbi.AllocationBase = mbi.BaseAddress;
- if ((mbi.AllocationBase == hModule) || (mbi.AllocationBase != mbi.BaseAddress) || (mbi.AllocationBase == NULL))
- nlen = 0;
- else
- {
- nlen = GetModuleFileNameA((HINSTANCE)mbi.AllocationBase, szModName, _countof(szModName));
- }
- if (nlen > 0)
- {
- char szBuf[MAX_PATH] = { 0 };
- wsprintfA(szBuf, "\n%p-%s", mbi.AllocationBase, szModName);
- printf("%s\n", szBuf);
- }
-
- pb += mbi.RegionSize;
- }
-
- fclose(fp);
- }
- break;
- case DLL_THREAD_ATTACH:
- case DLL_THREAD_DETACH:
- case DLL_PROCESS_DETACH:
- break;
- }
- return TRUE;
- }
3.2写一个测试程序
新建一个带界面的程序
改了一下窗口界面,这里加了一个菜单项注入。
点击注入出现弹窗,如下图所示。
输入进程ID,点击确定即可,下面是注入的回调函数。
- // “注入”框的消息处理程序。
- INT_PTR CALLBACK Inject(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
- {
- UNREFERENCED_PARAMETER(lParam);
- switch (message)
- {
- case WM_INITDIALOG:
- {
- return (INT_PTR)TRUE;
- }
- case WM_COMMAND:
- if (LOWORD(wParam) == IDOK)
- {
- //获取ID值
- DWORD processID = GetDlgItemInt(hDlg, IDC_EDIT1, NULL, FALSE);
- if (processID == 0)
- {
- processID = GetCurrentProcessId();
- }
-
- if (InjectLib(processID, TEXT("D://remoteDll.dll")))
- {
- printf("inject ok");
- EjectLib(processID, TEXT("D://remoteDll.dll"));
- }
- else
- {
- printf("inject failed");
- }
-
- EndDialog(hDlg, LOWORD(wParam));
- return (INT_PTR)TRUE;
- }
- else if (LOWORD(wParam) == IDCANCEL)
- {
- EndDialog(hDlg, LOWORD(wParam));
- return (INT_PTR)TRUE;
- }
- break;
- }
- return (INT_PTR)FALSE;
- }
InjectLib函数如下:
- BOOL WINAPI InjectLib(DWORD processID, PCWSTR pszLibFile)
- {
- BOOL bOk = FALSE;
- HANDLE hProcess = NULL, hThread = NULL;
- PWSTR pszLibFileRemote = NULL;
-
- __try
- {
- hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD |
- PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, processID);
-
- if (hProcess == NULL)
- __leave;
-
- int cch = 1 + lstrlenW(pszLibFile);
- int cb = cch * sizeof(wchar_t);
-
- pszLibFileRemote = (PWSTR)VirtualAllocEx(hProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);
- if (pszLibFileRemote == NULL)
- __leave;
-
- if (!WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFile, cb, NULL))
- __leave;
-
- FARPROC pfnThreadRtn = GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
- if (pfnThreadRtn == NULL)
- __leave;
-
- hThread = CreateRemoteThread(hProcess, NULL, 0, (PTHREAD_START_ROUTINE)pfnThreadRtn, pszLibFileRemote, 0, NULL);
- if (hThread == NULL)
- __leave;
-
- WaitForSingleObject(hThread, INFINITE);
-
- bOk = TRUE;
- }
- __finally
- {
- if (pszLibFileRemote != NULL)
- VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE);
-
- if (hThread != NULL)
- CloseHandle(hThread);
-
- if (hProcess != NULL)
- CloseHandle(hProcess);
- }
-
- return bOk;
- }
EjectLib函数如下:
- BOOL WINAPI EjectLib(DWORD processID, PCWSTR pszLibFile)
- {
- BOOL bOk = FALSE;
- HANDLE hthSnapshot = NULL;
- HANDLE hProcess = NULL, hThread = NULL;
- PWSTR pszLibFileRemote = NULL;
-
- __try
- {
- //获取指定进程的快照,以及这些进程使用的堆、模块和线程。
- hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID);
- if (hthSnapshot == INVALID_HANDLE_VALUE)
- __leave;
-
- MODULEENTRY32W me = { sizeof(me) };
- BOOL bFound = FALSE;
- BOOL bMoreMods = Module32FirstW(hthSnapshot, &me); //检索与进程关联的第一个模块的信息。
- for (; bMoreMods; bMoreMods = Module32NextW(hthSnapshot, &me)) //检索与进程或线程关联的下一个模块的信息
- {
- bFound = (_wcsicmp(me.szModule, pszLibFile) == 0) || (_wcsicmp(me.szExePath, pszLibFile));
- if (bFound)
- break;
- }
-
- if (!bFound)
- __leave;
-
- hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD |
- PROCESS_VM_OPERATION, FALSE, processID);
-
- if (hProcess == NULL)
- __leave;
-
- FARPROC pfnThreadRtn = GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary");
- if (pfnThreadRtn == NULL)
- __leave;
-
- //me.modBaseAddr 在所属进程的上下文中模块的基地址。
- hThread = CreateRemoteThread(hProcess, NULL, 0, (PTHREAD_START_ROUTINE)pfnThreadRtn, me.modBaseAddr, 0, NULL);
- if (hThread == NULL)
- __leave;
-
- WaitForSingleObject(hThread, INFINITE);
-
- bOk = TRUE;
- }
- __finally
- {
- if (hthSnapshot != NULL)
- CloseHandle(hthSnapshot);
-
- if (hThread != NULL)
- CloseHandle(hThread);
-
- if (hProcess != NULL)
- CloseHandle(hProcess);
- }
-
- return bOk;
- }
3.3执行程序
打开一个记事本程序,找到PID,如下图所示,PID = 20752。
运行3.2中的程序,输入这个PID,点击确定注入成功。
3.4运行结果
注入之后,在本地文件out.txt中,可以看见正在该记事本程序正在使用的DLL。