DLL注入是一种高级技术,通常用于调试、热补丁、日志记录、窗口子类化等。本文将聚焦于使用DLL注入实现窗口子类化。窗口子类化是通过改变窗口过程来重新定义窗口的行为。要改变窗口过程,该过程必须位于创建窗口的进程中。本文将展示如何在不通知创建窗口的进程的情况下,模仿黑客行为,打乱窗口的行为。本文涉及三个模块:被注入程序(Injectee)、注入DLL(Injection)和注入器(Injector)。
被注入程序是创建窗口的目标进程。本文中称之为“Injectee”,向其注入DLL(“Injection”)。这是一个简单的Win32应用程序,具有一个窗口,每当鼠标左键被按下时,就会在点击位置绘制一个圆圈。仅此而已!
这是一个DLL,它包含了新的窗口过程,将被挂钩到Injectee的窗口上。
这是实际将Injection注入到Injectee进程中的程序。这是一个简单的控制台应用程序。
如前所述,源代码分为三个模块。首先确保构建Injection模块,以便DLL准备好被注入。一旦Injection模块构建成功,生成的DLL被复制到x86目录。然后构建Injectee并启动它。现在,当使用鼠标左键点击屏幕时,圆圈会在点击位置绘制,并且它将继续正常工作。当构建器构建完成后,DLL从x86目录复制到Injectee项目的Debug/Release目录。构建Injector并启动它。注入器将把InjectionDLL注入到Injectee的进程中并终止。现在,如果左键点击屏幕,不会看到圆圈,而是会看到一个奇怪的消息“UR HACKED”。
Injectee的源代码没有什么特别的。它只是使用SDK创建窗口的普通代码。
如所知,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;
}
这段代码很简单。首先,需要知道要注入到目标进程(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 ] 这是文章的第一版。将根据观众的评论进行更新。