高级远程线程注入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位进程。需要特殊方式。