DLL注入技术示例:窗口子类化

DLL注入是一种高级技术,通常用于调试、热补丁、日志记录、窗口子类化等。本文将聚焦于使用DLL注入实现窗口子类化。窗口子类化是通过改变窗口过程来重新定义窗口的行为。要改变窗口过程,该过程必须位于创建窗口的进程中。本文将展示如何在不通知创建窗口的进程的情况下,模仿黑客行为,打乱窗口的行为。本文涉及三个模块:被注入程序(Injectee)、注入DLL(Injection)和注入器(Injector)。

被注入程序(Injectee)

被注入程序是创建窗口的目标进程。本文中称之为“Injectee”,向其注入DLL(“Injection”)。这是一个简单的Win32应用程序,具有一个窗口,每当鼠标左键被按下时,就会在点击位置绘制一个圆圈。仅此而已!

注入DLL(Injection)

这是一个DLL,它包含了新的窗口过程,将被挂钩到Injectee的窗口上。

注入器(Injector)

这是实际将Injection注入到Injectee进程中的程序。这是一个简单的控制台应用程序。

使用代码

如前所述,源代码分为三个模块。首先确保构建Injection模块,以便DLL准备好被注入。一旦Injection模块构建成功,生成的DLL被复制到x86目录。然后构建Injectee并启动它。现在,当使用鼠标左键点击屏幕时,圆圈会在点击位置绘制,并且它将继续正常工作。当构建器构建完成后,DLL从x86目录复制到Injectee项目的Debug/Release目录。构建Injector并启动它。注入器将把InjectionDLL注入到Injectee的进程中并终止。现在,如果左键点击屏幕,不会看到圆圈,而是会看到一个奇怪的消息“UR HACKED”。

Injectee的源代码

Injectee的源代码没有什么特别的。它只是使用SDK创建窗口的普通代码。

Injection的源代码

如所知,Injection是一个简单的DLL,当DllMain被调用并且原因为DLL_PROCESS_ATTACH时,正在执行黑客行为。

case DLL_PROCESS_ATTACH: { // 使用其标题查找Injectee的窗口 HWND hwnd = ::FindWindow(NULL,TEXT("Injectee")); // 如果找到窗口,则更改其窗口过程 if (hwnd) { oldWindowProc = ::SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR) HackingWndProc); } break; }

新的窗口过程看起来像这样:

LRESULT CALLBACK HackingWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_LBUTTONDOWN: { int x = GET_X_LPARAM(lParam); int y = GET_Y_LPARAM(lParam); HDC hDC = ::GetDC(hWnd); //::Rectangle(hDC, x, y, (x+50), (y+50)); DrawText(hDC, x, y); break; } case WM_RBUTTONDOWN: { int x = GET_X_LPARAM(lParam); int y = GET_Y_LPARAM(lParam); HDC hDC = ::GetDC(hWnd); ::Ellipse(hDC, x, y, (x+50), (y+50)); break; } case WM_DESTROY: { PostQuitMessage(0); break; } default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }

Injector的源代码

这段代码很简单。首先,需要知道要注入到目标进程(Injectee)中的DLL(Injection)的名称。这个DLL名称必须为目标进程所知。因此,这必须写入目标进程的地址空间。使用其窗口标题找到Injectee的窗口。接下来,使用GetWindowThreadProcessId获取创建窗口的进程句柄。使用其句柄打开Injectee的进程。然后在Injectee的地址空间中分配内存以写入要注入的DLL的名称。将DLL的名称写入Injectee。获取LoadLibrary方法的地址。这是将要在Injectee的地址空间中创建的线程将要调用的方法。然后调用CreateRemoteThread在目标进程中创建一个线程。这个线程将要执行LoadLibrary函数,并将库名称作为Injection DLL。由于线程调用LoadLibrary方法加载DLL,DllMain被调用并且原因为DLL_PROCESS_ATTACH。参考Injection的代码,看看当DllMain被调用时会发生什么。在调用VirtualFree方法之前等待远程线程执行,否则当远程线程查找DLL名称时,持有DLL名称的地址块已经被注入器进程释放,可能会导致崩溃!

int _tmain(int argc, _TCHAR* argv[]) { // 要注入到目标进程中的DLL名称 [ 这里之后称为 "Injectee" ] char *szInjectionDLLName = TEXT("Injection.dll"); DWORD dwProcessID; // 找到Injectee的主窗口 HWND hwnd = ::FindWindow(NULL,TEXT("Injectee")); // 获取Injectee的进程句柄 GetWindowThreadProcessId(hwnd, &dwProcessID); // 打开进程 HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID); // 在Injectee的地址空间中分配内存 void *baseAddress = VirtualAllocEx(hProcess, NULL, strlen(szInjectionDLLName), MEM_COMMIT, PAGE_READWRITE); // 将要注入的DLL名称写入Injectee SIZE_T nbBytesWritten = 0; WriteProcessMemory(hProcess, baseAddress, szInjectionDLLName, strlen(szInjectionDLLName), &nbBytesWritten); // 获取加载库的地址 FARPROC pLoadLib = GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryA"); // 在目标进程中创建远程线程 DWORD dwThreadID = 0; HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLib, baseAddress, 0, &dwThreadID); // 等待远程线程完成其工作。 // 否则,下一条语句 [ VirtualFreeEx ] 将被调用 // 这将释放目标进程中的地址 // 当远程线程引用地址时会导致崩溃!!! WaitForSingleObject(hRemoteThread,INFINITE); // 现在释放在Injectee的地址空间中分配的内存 VirtualFreeEx(hProcess, baseAddress, 0, MEM_RELEASE); // DONE!!!!!!!!!!!. return 0; }

版本1:[ 02/13/2013 ] 这是文章的第一版。将根据观众的评论进行更新。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485