- CreateRemoteThread
创建一个在其它的进程的虚拟地址空间中运行的线程。
使用CreateRemoteThreadEx 函数来创建一个在其它的进程的虚拟地址空间中运行的线程并可选的指定一个特定的扩展属性。
HANDLE WINAPI CreateRemoteThread(
_In_ HANDLE hProcess,
_In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_ LPDWORD lpThreadId
);
hProcess
指定即将在其内部创建一个线程的目标进程,该句柄必须拥有PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, 和 PROCESS_VM_READ 访问权限。
lpThreadAttributes
SECURITY_ATTRIBUTES 结构体指针,指定新创建的线程的安全属性,以及函数返回的子线程的句柄是否能够被子进程继承。如果该参数为NULL,得到一个默认的安全属性,且句柄不可以被继承。线程的默认的安全描述符的ACL 来自于创建者的主令牌。
Windows XP:一个线程的默认ACL 来自于创建者的主或者模仿令牌。Windows XP SP2 和 Windows Server 2003 开始改变。
dwStackSize
栈的初始大小,字节对齐。详细请参考https://msdn.microsoft.com/en-us/library/windows/desktop/ms686774(v=vs.85).aspx
lpStartAddress
远程线程的起始地址。
lpParameter
传递给远程线程的指针
dwCreateFlags
控制线程创建的标志
值 | 含义 |
---|---|
0 | 线程创建之后立马执行 |
CREATE_SUSPENDED | 暂停的线程,需要调用ResumeThread 恢复其执行 |
STACK_SIZE_PARAM_IS_A_RESERVATION | dwStackSize 参数指定了栈的初始预留的大小,如果没有指定这个参数的话,dwStackSize 参数指定的是提交的大小 |
lpThreadId
用于接收线程ID。可为NULL
成功的话,返回值是新创建的线程的句柄。失败返回NULL。
即使lpStartAddress 指向的内存位置非法,CreateRemoteThread 也可能调用成功。当新线程开始执行的时候,如果起始地址非法,触发异常并导致线程退出。非法的起始地址导致的线程终止会导致线程所在的进程错误退出。这比较像CreateProcess 的异步属性,即使创建的进程会有DLL 丢失的情况,该创建操作也会成功。
终端服务将每个会话隔离起来了,因此,如果目标进程和调用者进程在不同的会话中,CreateRemoteThread 调用将失败。
当我们不为新创建的线程提供安全描述符的时候,函数返回的线程句柄拥有对于新创建的线程的所有的访问权限,如果我们提供了,之后对于该线程句柄的操作将会通过ACL 的检查,如果检查失败的话,请求该操作的进程将无法以期待的方式访问该线程。
如果创建线程的时候没有指定暂停标志,新创建的线程在CreateRemoteThread 函数返回之前就已经开始执行了。
ExitProcess、ExitThread、CreateThread、CreateRemoteThread 函数和正在开始执行的进程,它们在同一个进程内的操作都被同步了。在一个地址空间中,上面列举出的事件中,只有一个在发生。这意味着,下面的规则必须被遵守。
- 在进程开始以及DLL 初始化函数中,新线程可以被创建,但是直到进程的DLL 初始化完成,它们都得不到执行。
- 一个进程中只能有一个线程在进行DLL 初始化或者派遣例程。
- 直到所有的线程完成了它们的DLL 初始化或者派遣例程之后,ExitProcess 函数才会退出。
- RtlCreateUserThread
typedef NTSTATUS (WINAPI *LPFUN_RtlCreateUserThread)(
HANDLE, // ProcessHandle
PSECURITY_DESCRIPTOR, // SecurityDescriptor (OPTIONAL)
BOOLEAN, // CreateSuspended
ULONG, // StackZeroBits
SIZE_T, // StackReserved
SIZE_T, // StackCommit
PVOID, // StartAddress
PVOID, // StartParameter (OPTIONAL)
PHANDLE, // ThreadHandle
PCLIENT_ID // ClientID
);
这个函数可以实现跨会话创建线程。唯一的问题就是:当我们在Vista 之前的操作系统调用此函数所创建的线程并没有通知给csrss 进程,它没有完整的初始化,在调用一些网络或者其它的系统函数的时候他可能返回失败,而通过CreateRemoteThread 创建的线程没有任何问题,在Vista+的操作系统上调用此函数也没有问题。因此我们需要判断系统版本,当目标系统为Vista-的时候,我们应该通过RtlCreateUserThread创建一个挂起的线程,然后调用CsrClientCallServer通知csrss 这个线程的创建,然后csrss 做相应的记录及初始化工作之后,我们再恢复线程的执行。最后,我们新创建的线程必须自己调用ExitThread退出,否则也会产生崩溃的情况。
- NtCreateThreadEx
typedef DWORD64 (WINAPI *PFNTCREATETHREADEX)
(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD64 dwStackSize,
DWORD64 dw1,
DWORD64 dw2,
LPVOID Unknown
);
在Windows 2000 和 XP SP 0,1 上我们使用CreateRemoteThread,在Vista+ 上我们使用NtCreateThreadEx。
在XP SP2 上,我们可以按照RtlCreateUserThread + CsrClientCallServer 的方式创建远程线程。
- 代码示例
// 下面这个函数就是调用NtCreadThreadEx 创建远程线程
HANDLE MyCreateRemoteThread(HANDLE hProcess, LPTHREAD_START_ROUTINE pThreadProc, LPVOID pRemoteBuf)
{
HANDLE hThread = NULL;
FARPROC pFunc = NULL;
// 只有Vista 之后操作系统可用此函数
if(IsVistaOrLater())
{
pFunc = GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "NtCreateThreadEx");
if( pFunc == NULL )
{
ShowMessage(_T("无法得到NtCreateThreadEx 函数地址"));
ErrorShow();
return FALSE;
}
((PFNTCREATETHREADEX)pFunc)(
&hThread,
0x1FFFFF, // 内核调试的结果就是这个参数
NULL,
hProcess,
pThreadProc,
pRemoteBuf,
FALSE,
NULL,
NULL,
NULL,
NULL);
if( hThread == NULL )
{
ErrorShow();
return FALSE;
}
return hThread;
}
else
{
ShowMessage(_T("版本错误\r\n无NtCreateThreadEx 函数"));
return FALSE;
}
}
// 使用CreateRemoteThread 创建远程线程的操作比较简单直接调用即可,毕竟是文档化的函数
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf,0, NULL);
// RtlCreateUserThread 的使用
#define STATUS_SUCCESS ((NTSTATUS) 0x00000000)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xC0000004)
#define STATUS_IO_DEVICE_ERROR ((NTSTATUS) 0xC0000185)
struct THREAD_INFO
{
HANDLE hThread;
CLIENT_ID id;
};
struct CSRSS_MESSAGE
{
ULONG Unknown1;
ULONG Opcode;
ULONG Status;
ULONG Unknown2;
};
struct PORT_MESSAGE
{
ULONG u1;
ULONG u2;
union
{
CLIENT_ID ClientId;
float DoNotUseThisField;
};
ULONG MessageId;
union
{
ULONG ClientViewSize;
ULONG CallbackId;
};
};
struct CSR_API_MESSAGE
{
PORT_MESSAGE PortMessage;
CSRSS_MESSAGE CsrssMessage;
THREAD_INFO ThreadInfo;
};
typedef CLIENT_ID *PCLIENT_ID;
typedef PVOID PUSER_THREAD_START_ROUTINE;
typedef NTSTATUS (NTAPI *pfnInitSubSystem) (
IN OUT CSR_API_MESSAGE* ApiMessage,
IN OUT struct CSR_CAPTURE_BUFFER* CaptureBuffer,
IN ULONG ApiNumber,
IN ULONG DataLength );
LPTHREAD_START_ROUTINE AllocWritePath(HANDLE hTargetProcHandle, LPCWSTR dllPath, LPVOID *lpExecParam) {
LPVOID lpDllAddr = NULL;
LPVOID lpWriteVal = NULL;
LPVOID loadLibAddr = NULL;
#ifdef _WIN64
if( lpDllAddr = VirtualAllocEx(hTargetProcHandle, NULL, wcslen(dllPath)*sizeof(WCHAR) + sizeof(WCHAR), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE))
{
if (WriteProcessMemory(hTargetProcHandle, lpDllAddr, dllPath, wcslen(dllPath)*sizeof(WCHAR) + sizeof(WCHAR), NULL) == 0) {
ErrorShow();
return NULL;
}
*lpExecParam = (LPVOID *)lpDllAddr;
loadLibAddr = GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryW");
}
#else
BYTE StaticShellCode[31] = { 0xE8,0,0,0,0, // call (5字节) 4
0x5D, // pop ebp 5
0x8B,0xC5, // mov eax,ebp 7
0x83,0xC0,0x1A, // add eax,1a 10
0x50, // push eax 11
0xB8,0,0,0,0, // mov eax,LoadLibraryW 16
0xFF,0xD0, // call eax 18
0x6A,0, // push 0 20
0xB8,0,0,0,0, // mov eax,ExitThread 25
0xFF,0xD0, // call eax 27
0xC3, // ret 4 28
0};
BYTE* ShellCode = new BYTE[wcslen(dllPath)*sizeof(WCHAR)+sizeof(WCHAR) + 31];
CopyMemory(ShellCode,StaticShellCode,31);
CopyMemory(&ShellCode[31],dllPath,wcslen(dllPath)*sizeof(WCHAR) + sizeof(WCHAR));
PVOID lpCunc = GetProcAddress(GetModuleHandle("kernel32.dll"),"LoadLibraryW");
CopyMemory(&ShellCode[13],&lpCunc,sizeof(PVOID));
lpCunc = GetProcAddress(GetModuleHandle("kernel32.dll"),"ExitThread");
CopyMemory(&ShellCode[22],&lpCunc,sizeof(PVOID));
LPVOID pRemoteCode = NULL;
if(pRemoteCode = VirtualAllocEx(hTargetProcHandle, NULL, wcslen(dllPath)*sizeof(WCHAR) + sizeof(WCHAR)+31, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE))
{
if (WriteProcessMemory(hTargetProcHandle, pRemoteCode, ShellCode, wcslen(dllPath)*sizeof(WCHAR) + sizeof(WCHAR) + 31, NULL) == 0) {
ErrorShow();
return NULL;
}
*lpExecParam = 0;
loadLibAddr = (LPVOID)pRemoteCode;
}
#endif
return (LPTHREAD_START_ROUTINE) loadLibAddr;
}
BOOL MyNtCreateThreadEx(CONST WCHAR* szDllPath,DWORD ProcessId)
{
BOOL bRet = FALSE;
OSVERSIONINFO osver;
HANDLE ProcessHandle = NULL;
LPVOID lpStartExecAddr = NULL;
LPVOID lpExecParam = NULL;
osver.dwOSVersionInfoSize = sizeof(osver);
if (GetVersionEx(&osver)) {
if (osver.dwMajorVersion == 5) {
ProcessHandle = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD, 0, ProcessId );
}
if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 0) {
printf("\t[+] Detected Windows Vista\n");
ProcessHandle = NULL;
}
if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 1) {
ProcessHandle = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, ProcessId );
}
if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 2) {
ProcessHandle = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, ProcessId );
}
} else {
printf("\n[!] Could not detect OS version\n");
}
if(ProcessHandle != NULL)
{
if(lpStartExecAddr = AllocWritePath(ProcessHandle, szDllPath, &lpExecParam))
{
printf("%p\r\n",lpStartExecAddr);
LPVOID rtlCreateUserAddr = NULL;
HANDLE hRemoteThread = NULL;
CLIENT_ID cid;
if( rtlCreateUserAddr = GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlCreateUserThread") ) {
LPFUN_RtlCreateUserThread funRtlCreateUserThread = (LPFUN_RtlCreateUserThread)rtlCreateUserAddr;
// hRemoteThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)lpStartExecAddr,NULL,0,NULL); 成功
// hRemoteThread = CreateRemoteThread(ProcessHandle, NULL, 0, (LPTHREAD_START_ROUTINE)lpStartExecAddr, lpExecParam,0, NULL); 成功
// call RtlCreateUserThread to create suspended thread, then CsrClientCallServer(enable win32 subsystem support) and then resume thread
// 之前失败?----XP需要通过CsrClientCallServer enable win32 subsystem support.
#ifndef _WIN64
// create suspended thread
funRtlCreateUserThread(
ProcessHandle, // ProcessHandle
NULL, // SecurityDescriptor (OPTIONAL)
1, // CreateSuspended
0, // StackZeroBits
0, // StackReserved
0, // StackCommit
(PVOID) lpStartExecAddr,// StartAddress
(PVOID) lpExecParam,// StartParameter (OPTIONAL)
&hRemoteThread, // ThreadHandle
&cid // ClientID
);
if (hRemoteThread == NULL) {
ErrorShow();
}
else
{
CSR_API_MESSAGE csrmsg = { { 0 }, { 0 }, { hRemoteThread, cid } };
// enable win32 subsystem support
pfnInitSubSystem CsrClientCallServer = (pfnInitSubSystem)GetProcAddress(GetModuleHandle(_T("ntdll.dll")),"CsrClientCallServer");
// Resume
NTSTATUS status = CsrClientCallServer( &csrmsg, NULL, 0x10001u, sizeof( THREAD_INFO ) );
if (status == STATUS_SUCCESS)
{
ResumeThread(hRemoteThread);
bRet = TRUE;
}
}
#else
funRtlCreateUserThread(
ProcessHandle, // ProcessHandle
NULL, // SecurityDescriptor (OPTIONAL)
0, // CreateSuspended
0, // StackZeroBits
0, // StackReserved
0, // StackCommit
(PVOID) lpStartExecAddr,// StartAddress
(PVOID) lpExecParam,// StartParameter (OPTIONAL)
&hRemoteThread, // ThreadHandle
&cid // ClientID
);
if(hRemoteThread == NULL || hRemoteThread == INVALID_HANDLE_VALUE)
{
ErrorShow();
}
else
{
ShowMessage(_T("%d"),GetThreadId(hRemoteThread));
//ResumeThread(hRemoteThread);
bRet = TRUE;
}
#endif
}
}
}
return bRet;
}
// 当我们注入提权的进程的时候可能需要提升自己的权限,其中DEBUG 权限是可以满足我们要求的权限
BOOL EnableDebugPrivilegeX86(BOOL bEnable = TRUE) //它是CRemThreadInjector类的静态成员
{ //附给本进程特权,以便访问系统进程
BOOL bOk = FALSE;
HANDLE hToken;
//打开一个进程的访问令牌
if(::OpenProcessToken(::GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken))
{ //取得特权名称为"SetDebugPrivilege"的LUID
LUID uID;
::LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&uID);
//调整特权级别
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = uID;
tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
::AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL);
bOk = (::GetLastError() == ERROR_SUCCESS);
//关闭访问令牌句柄
::CloseHandle(hToken);
}
return bOk;
}
BOOL EnableDebugPrivilegeX64()
{
DWORD dwRetVal;
RtlAdjustPrivilege=(pfnRtlAdjustPrivilege64)GetProcAddress((HMODULE)(GetModuleHandle(_T("ntdll.dll"))),"RtlAdjustPrivilege");
if(RtlAdjustPrivilege == NULL)
{
return FALSE;
}
/**
.常量 SE_BACKUP_PRIVILEGE, "17", 公开
.常量 SE_RESTORE_PRIVILEGE, "18", 公开
.常量 SE_SHUTDOWN_PRIVILEGE, "19", 公开
.常量 SE_DEBUG_PRIVILEGE, "20", 公开
*/
return RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE,1,0,&dwRetVal);
}
BOOL EnableDebugPrivilegeX86X64()
{
#ifdef _WIN64
return EnableDebugPrivilegeX64();
#else
return EnableDebugPrivilegeX86();
#endif
}