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. CreateRemoteThreadYou can create a thread in another process using the API.
CreateRemoteThreadIf you browse on MSDN, you'll see the following in Remarks:

 
CreateRemoteThread function (processthreadsapi.h)-Win32 apps
Creates a thread that runs in the virtual address space of another process.
docs.microsoft.com
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 CreateRemoteThreadhas been a crisis in DLL Injection using this widely used method, but people have NtCreateThreadExfound out that it can be replaced using a function called a sub-function However, it NtCreateThreadExwas 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 NtCreateThreadExDLL Injection was able to succeed through the function.
Over the years, Windows 10 was released and the NtCreateThreadExsound 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.
Win7 64
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.
Windows 10 x64 1057
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. 

Sample source


 
lucidmaj7 / DLLInjection
DLL Injection Test App. Contribute to lucidmaj7 / DLLInjection development by creating an account on GitHub.
github.com

 

Reference


 
DLL Injection (3)-Using Injector
If you make only the injector, it seems to be the easiest way to do DLL injection. [Basic Concept of DLL Injection] [DLL Injection Using EAT] The process of creating an injector is much simpler than I thought. First target process ...
eram.tistory.com

 
DLL Injection in kernel 6
DLL Injection in kernel 6 As of kernel version 6, CreateRemoteThread () is virtually unavailable. It is said that if you use ZwCreateThreadEx () API instead of this API, it becomes Injection. Windows 10 x64
kblab.tistory.com

 
DLL injection with CreateRemoteThread
DLL injection with CreateRemoteThread dll-injection hacking windows 15 Dec 2018 Table of Contents IntroductionBackgroundDebugging APIRegistryCreateRemoteThreadHookMechanismx86_64 / WOW64 ProblemResultInjecting DllFuture WorkConclusion ChangeLog 2018/12/19: Dll
gwangyi.github.io

Comments

Popular posts from this blog

Enabling Com Port on Hyper-V 2nd Generation VM / Windbg

Using Overlapped I / O in DeviceIoControl