Back for more? Good. I learned quite a bit doing the research for this portion of the series, and I have to give credit mostly to my sources. Check out the Open Security Research and Infosec Institute articles in the references. They go really in depth on this topic. I am not really expanding on their content, but I find that spending time explaining it helps me to better understand the information. Hopefully having another resource will help you to better understand as well.
CreateRemoteThread Injection Theory
In the last blog, we took a look at the SetWindowsHookEx method for injection. It got the job done, but it wasn’t very pretty. To inject into a process in which we wanted to run our DLL, we had to get a thread id for the process to inject or it would inject into any process that received the hook signal. Luckily there are better ways to do this. In this post, we will examine CreateRemoteThread. With SetWindowsHookEx, we used LoadLibrary to load the DLL into the current (not target) process space. Then we got the address to our method using GetProcAddress. Then by the nature of how SetWindowsHookEx works, we create a hook, hook a process, which will automatically load our DLL into it and ta-da! We have achieved DLL injection. CreateRemoteThread behaves differently though. Here are the steps:
- Create space for the path to our DLL in the target process’ memory using VirtualAllocEx.
- Write the DLL path into the memory we just allocated using WriteProcessMemory.
- Once we have the DLL path in memory we use CreateRemoteThread (or other undocumented features) which uses LoadLibraryA to inject the DLL into the target process.
Pretty straightforward, right? Even more interesting are the undocumented features.
The Dark Side of the Windows Features
Undocumented features in Windows are those features on which Microsoft has not released information. There are several issues with using them. The most obvious problem is that we don’t have documentation on specific functionality. Fortunately, the ReactOS project has documented a lot of these features for us and this page gives us quick reference to them. Other issues include the fact that these features may eventually go out of use without notification from Microsoft. Finally, they all require a bit more code and understanding to use properly.
Considering all of these problems, why use the undocumented features at all? The primary reason is that since Vista, CreateRemoteThread will fail if the target is in a different session than the current one. The undocumented features do not. They are also less immediately understandable from a reverse engineering point of view, and that’s a bonus. Finally, it’s just plain fun messing with some of the other lesser known functionality in Windows.
In our code, we will use CreateRemote thread along with the two undocumented functions NtCreateThreadEx and RtlCreateUserThread. You may have heard of Mimikatz and Metasploit. Both use RtlCreateUserThread for their DLL injection. If you want to see their code, Mimikatz can be found here, and Meterpreter is here. It’s worth mentioning that the Mimikatz blog is in French, so if you don’t speak the language it is also well documented here.
Between the two functions, which should we pick? NtCreateThreadEx is a syscall, that is a method for a user space application to talk to the kernel. Let’s take a quick look at RtlCreateUserThread in IDA. Pull up ntdll.dll in IDA, go to the names tab and find RtlCreateUserThread. Go to it and you should see something similar to the following.
Follow the call to see this:
When you trace the code down you see RtlCreateUserThread calls NtCreateThreadEx. So RtlCreateUserThread appears to be a small wrapper for NtCreateThreadEx. The reason we would want to call RtlCreateUserThread is that the syscall option for NtCreateThreadEx could change between versions of Windows. Therefore, it will be more likely to work if we use RtlCreateUserThread. Mimikatz and Meterpreter use RtlCreateUserThread so it is safe to go with that option.
I have modified the code from the references below. We’re going to start by stepping through using plain, old CreateRemoteThread. Taking the steps from above we have:
1. Create space for our DLL path in the target process’ memory using VirtualAllocEx.
2. Write the DLL path into the memory we just allocated using WriteProcessMemory.
3. Once we have the DLL in memory we use CreateRemoteThread (or other undocumented features) which uses LoadLibraryA to inject the DLL into the target process.
Here is what it looks like when we use that for injection (the code for the output is below).
The next step is to use the undocumented methods instead of CreateRemoteThread. To start, we need some way to call our method. With CreateRemoteThread, we can just call it because it is part of the Windows API. These other ones don’t have that ability so we need to create the template for it. We will work with RtlCreateUserThread to start. First we create a method by the same name (it doesn’t have to be the same but it’s clearer this way). This method declares a prototype for the function. This prototype matches the template given to us by NtInternals. Then we create a handle to a thread, since RtlCreateUserThread takes a pointer to that handle as an input and sets it to the handle to the thread it creates. Then we get the handle to ntdll.dll as that is where RtlCreateUserThread is stored. Many functions in the different Windows DLLs are exported so that we can use them natively. We can export functions using the “__declspec(dllexport)” keyword in our DLLs in the same way. The undocumented ones are not exported though, so we have to get the handle and get the address to it. The next step is to get the address to the process using GetProcAddress. Finally we call it and return the handle to our thread. Here is our template for that.
Luckily the process is essentially the same for NtCreateThreadEx, as we can see below.
Here is everything combined! Not too intimidating now is it?
This is the output of running it and Process Hacker’s output for the dll’s loaded. Looking good!
Reverse Engineering, A Slight Return
Now we will get the chance to look at our payload from the receiving end. Once you compile it, pull it up in IDA and let’s see what we have. I am no IDA expert, but luckily this isn’t too complicated (especially with the code in front of us!). First thing I would do is pull up the names window. In there we see the following:
One thing to note when you see these, is that the CreateRemoteThread has the “I” before it and the other two have the “A”. The “I” stands for imported names and the “A” is ASCII. ASCII is what the function name was when compiling. So while we named these two by their appropriate name, someone who is malicious would probably obfuscate them. Double click on the one of your choice and you should be brought to something like this:
CreateRemoteThread was imported so that’s why it is part of the idata section. The rdata is the read only version of the data segment with our method. If you right click on either, though, you can bring up this menu where you can jump to the xref to the method.
That will allow you to get to the actual code of the method. I prefer the graph view so that’s what I use primarily. Side by side as seen below, you will notice that the two undocumented functions look very similar (which is unsurprising). In order they are CreateRemoteThread, NtCreateThreadEx, RtlCreateUserThread. CreateRemoteThread is kindly labeled for us making it easier to follow the assembly, but ultimately most method calls will look pretty close to each other.
I hope you found a lot of useful information here. If you have any questions or comments, send me an email at firstname.lastname@example.org. Coming up in the next blog we will take a look at reflective dll injection and another method that we missed here. Keep hacking!