DLL Injection using CreateRemoteThread in Windows 10
DLL Injection using CreateRemoteThread
One of the methods of DLL injection is to create a RemoteThread and load the desired DLL into the target process. This is one of the simplest and most widely used methods.
CreateRemoteThread
You can create a thread in another process using the API.CreateRemoteThread
If you browse on MSDN, you'll see the following in Remarks:Terminal Services isolates each terminal session by design. Therefore, CreateRemoteThread fails if the target process is in a different session than the calling process.
In translation, Terminal Services Pros say that if the target process is running in a different session, it will fail because the session is specified and executed differently. After Windows Vista, that is, since NT Kernel 6.x, user applications and services run, the concept of session was introduced.
There
CreateRemoteThread
has been a crisis in DLL Injection using this widely used method, but people have NtCreateThreadEx
found out that it can be replaced using a function called a sub-function . However, it NtCreateThreadEx
was an undocumented function, and it was a pointless API when suddenly impossible to call. Anyway CreateRemoteThread
, when DLL Injection was executed to another session from Windows 7, it failed with returning LastError of 0x08, but NtCreateThreadEx
DLL Injection was able to succeed through the function.
Over the years, Windows 10 was released and the
NtCreateThreadEx
sound of DLL Injectino being impossible began to burst again. You can see a lot of articles by just searching on Google. It is telling you to use RtlCreateUserThread as a replacement for NtCreateThreadEx.
I tested it myself.
The following is example code to perform DLL Injection.
BOOL DLLInjectByRemoteThread(DWORD dwPID, LPCWSTR lpszDLLPath )
{
HMODULE hKernel32 = NULL;
HMODULE hNTDLL = NULL;
FARPROC lpfnLoadLibrary = NULL;
pfnNtCreateThreadEx fpNtCreateThread = NULL;
HANDLE hTargetProc = NULL;
LPVOID dllPathAlloc = NULL;
BOOL bSuccess = TRUE;
HANDLE hThread = NULL;
if (wcslen(lpszDLLPath) == 0)
{
bSuccess = FALSE;
goto EXIT;
}
if (!PathFileExists(lpszDLLPath))
{
bSuccess = FALSE;
OutputDebugString(_T("Error: DLL File is not exists"));
goto EXIT;
}
hTargetProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
if (!hTargetProc)
{
bSuccess = FALSE;
OutputDebugString(_T("Error: fail to open Target process "));
goto EXIT;
}
dllPathAlloc = VirtualAllocEx(hTargetProc, NULL, sizeof(WCHAR) * MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
if (dllPathAlloc == NULL)
{
OutputDebugString(_T("Error: VirtualAllocEx "));
bSuccess = FALSE;
goto EXIT;
}
if (!WriteProcessMemory(hTargetProc, dllPathAlloc, lpszDLLPath, sizeof(WCHAR) * wcslen(lpszDLLPath) +sizeof(WCHAR), NULL))
{
OutputDebugString(_T("Error: WriteProcessMemory "));
bSuccess = FALSE;
goto EXIT;
}
hKernel32=LoadLibrary(L"Kernel32.dll");
if (!hKernel32)
{
OutputDebugString(_T("Error: LoadLibrary Kernel32.dll "));
bSuccess = FALSE;
goto EXIT;
}
lpfnLoadLibrary = GetProcAddress(hKernel32, "LoadLibraryW");
if (!lpfnLoadLibrary)
{
OutputDebugString(_T("Error: GetProcAddress LoadLibraryW "));
bSuccess = FALSE;
goto EXIT;
}
hThread = CreateRemoteThread(hTargetProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpfnLoadLibrary, dllPathAlloc, 0, NULL);
if (hThread == NULL)
{
OutputDebugString(_T("Error: CreateRemoteThread fail "));
DWORD dwError = GetLastError();
if (dwError == 5)
{
OutputDebugString(_T("Error: CreateRemoteThread GetLastError 5 "));
bSuccess = FALSE;
goto EXIT;
}
if (dwError == 8)
{
OutputDebugString(_T("Error: CreateRemoteThread GetLastError 8 "));
hNTDLL = LoadLibrary(L"ntdll.dll");
if (!hNTDLL)
{
bSuccess = FALSE;
goto EXIT;
}
CLIENT_ID cid;
pfnRtlCreateUserThread RtlCreateUserThread = (pfnRtlCreateUserThread)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlCreateUserThread");
if (RtlCreateUserThread)
{
NTSTATUS status = 0;
status = RtlCreateUserThread(hTargetProc, NULL, FALSE, 0, 0, 0, (LPTHREAD_START_ROUTINE)lpfnLoadLibrary, dllPathAlloc, &hThread, &cid);
if (NT_SUCCESS(status) && hThread != NULL)
{
OutputDebugString(_T("success RtlCreateUserThread"));
}
else
{
OutputDebugString(_T("fail RtlCreateUserThread"));
bSuccess = FALSE;
goto EXIT;
}
}
else
{
bSuccess = FALSE;
goto EXIT;
}
}
}
OutputDebugString(_T("success inject"));
if (hThread)
{
WaitForSingleObject(hThread, INFINITE);
}
EXIT:
if (dllPathAlloc)
{
VirtualFreeEx(hTargetProc, dllPathAlloc, 0, MEM_RELEASE);
}
if (hTargetProc)
{
CloseHandle(hTargetProc);
}
if (hThread)
{
CloseHandle(hThread);
}
if (hNTDLL)
{
FreeLibrary(hNTDLL);
}
if (hKernel32)
{
FreeLibrary(hKernel32);
}
return bSuccess;
}
Once tested on Windows 7 SP1 x64.
In the first attempt, Injection using CreateRemoteThread spits GetLastError 8 and the injection fails and tries again with RtlCreateRemoteThread to succeed. In other words, in Windows7, it is not possible to inject into another session's process with CreateRemoteThread.
I tried it again on Windows 10 x64 1057 version.
In Windows 10, injection is done directly through the CreateRemoteThread function.
Of course, the process protected by the OS or protected by a protection driver, etc., cannot be injected. However, unlike the description of MSDN, in the case of the latest Windows 10, injection was possible with CreateRemoteThread as in previous XP. Also, injection was possible through NtCreateThread and RtlCreateUserThread. Whether Microsoft has not reflected these issues on MSDN, it is only a bug or a confusion.
Comments
Post a Comment