高級遠程線程注入NtCreateThreadEx
一丶簡介
在Windows下NtCreateThreadEx
是CreateRemoteThread
的底層函數。RtlCreateUserThread
也是對 NtCreateThreadEx的一層包裝
所以着重一下研究NtCreateThreadEx
函數
二丶原型
2.1 函數原型
NtCreateThreadEx
在32位下和64位下函數原型不一致。
結構如下:
#ifdef _AMD64_
typedef DWORD(WINAPI* PfnZwCreateThreadEx)(
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 MaximunStackSize,
LPVOID pUnkown);
#else
typedef DWORD(WINAPI *PfnZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateThreadFlags,
DWORD ZeroBits,
DWORD StackSize,
DWORD MaximumStackSize,
LPVOID pUnkown);
#endif // DEBUG
如果要想使用 NtCreateThreadEx
函數。那么就需要從NtDll
中以動態的方式導出使用。
2.2 遠程線程注入代碼
遠程線程代碼注入分為如下幾個步驟
- OpenProcess 打開要注入的進程
- VirtualAllocEx 在被注入的進程中申請讀寫內存
- WriteProcessMemory 寫入DLL路徑到申請的內存中
- VirtualProtectEx 修改內存保護屬性,這一步可以不需要使用。
- CreateRemoteThread 創建遠程線程,高級遠程線程注入可以 將此函數 替換為
NtCreateThreadEx
- WaitForSingleObject 等待過程完成
完整偽代碼如下:
BOOLEAN RemoteInject(DWORD pid, LPWSTR wszInjectDllPathName,ULONG uDllPathSize)
{
HANDLE hProc = NULL;
LPVOID lpBuffer = NULL;
SIZE_T dwWriteBytes = 0;
DWORD dwRetErrorCode = 0;
HANDLE hThreadHandle = NULL;;
PVOID pfnLoadLibraryW = NULL;
HMODULE ntdll = NULL;
HMODULE k32 = NULL;
bool bIsOk = FALSE;
do {
ntdll = LoadLibrary(TEXT("ntdll.dll"));
if (ntdll == NULL)
{
break;
}
k32 = LoadLibrary(TEXT("kernel32.dll"));
if (k32 == NULL)
{
break;
}
m_ZwCreateThreadEx = reinterpret_cast<PfnZwCreateThreadEx>(GetProcAddress(ntdll, "ZwCreateThreadEx"));
if (NULL == m_ZwCreateThreadEx)
{
break;
}
pfnLoadLibraryW = reinterpret_cast<PVOID>(::GetProcAddress(k32, "LoadLibraryW"));
if (pfnLoadLibraryW == NULL)
{
break;
}
hProc = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
if (hProc == NULL)
{
break;
}
lpBuffer = VirtualAllocEx(hProc, 0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NULL == lpBuffer)
{
break;
}
dwRetErrorCode = WriteProcessMemory(hProc, lpBuffer, wszInjectDllPathName, uDllPathSize, &dwWriteBytes);
if (0 == dwRetErrorCode)
{
break;
}
m_ZwCreateThreadEx(&hThreadHandle, PROCESS_ALL_ACCESS, NULL, hProc, (LPTHREAD_START_ROUTINE)pfnLoadLibraryW, lpBuffer, 0, 0, 0, 0, NULL);
WaitForSingleObject(hProc, 2000);
if (NULL == hThreadHandle)
{
break;
}
bIsOk = TRUE;
} while (FALSE);
if (NULL != lpBuffer)
{
VirtualFreeEx(hProc, lpBuffer, 0, MEM_RELEASE);
lpBuffer = NULL;
}
if (NULL != hProc)
{
CloseHandle(hProc);
hProc = NULL;
}
if (NULL != hThreadHandle)
{
CloseHandle(hThreadHandle);
hThreadHandle = NULL;
}
if (k32 != NULL)
{
FreeLibrary(k32);
k32 = NULL;
}
if (ntdll != NULL)
{
FreeLibrary(ntdll);
ntdll = NULL;
}
return bIsOk;
}
注意: uDllPathSize 是DLL全路徑的空間長度。 如果是寬字符一定要 wcslen(str) * 2
才可以。
代碼經過驗證 32位程序可以注入DLL到32位的進程。 64位進程可以注入dll到64位進程。
32位進程不可注入DLL到64位進程。需要特殊方式。