高级远程线程注入NtCreateThreadEx

高级远程线程注入NtCreateThreadEx

一丶简介

在Windows下NtCreateThreadExCreateRemoteThread的底层函数。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位进程。需要特殊方式。

posted @   iBinary  阅读(3458)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 字符编码:从基础到乱码解决
点击右上角即可分享
微信分享提示