Offensive Nim DLL Injection

I’ve been learning Nim programming and love how easy and productive Nim development is compared to C/C++, while also giving you low-level control and a fantastic Windows FFI. There are already some great examples out there on Offensive Nim, including Byt3bl33d3r’s OffensiveNim GitHub repository. I recently found that there didn’t seem to be any examples of how to use Nim to inject a DLL from disk into a process. After spending more than a day debugging my code, I stumbled on waldo-irc’s NimMusings example. This post is a simple explanation on the Windows API call differences between injecting shellcode and DLL’s into a process using Nim.

When injecting shellcode (or a DLL), there are four basic steps:

  1. Get a handle to a process (pHandle in the image below)
  2. Allocate memory (VirtualAllocEx)
  3. Write memory (WriteProcessMemory)
  4. CreateRemoteThread

Referring to the following image, the first line between the shellcode example on the left, and the DLL example on the right is in VirtualAllocEx. In the shellcode example, the third parameter is the shellcode length. In the DLL example, the length of the path to the DLL on disk is used. (Not shown in the graphic). In this case the path to the DLL is passed in on the command line, and in the function signature (not shown) var name: string = "C:\\Path\\To\\Dll.dll" and the len function returns the length of the string.

In the next section in the WriteProcessMemory function, in the shellcode example on the left the third parameter is a pointer to the start of the shellcode followed by the length of the shellcode. In the DLL example on the right, a pointer to the start of the path/name to the dll is used, followed by the length of that string.

In the final section, in the shellcode example the CreateRemoteThread call uses the LPTHREAD_START_ROUTINE with a pointer (rPtr) to the shellcode written in memory (let rPtr = VirtualAllocEx…) followed by a NULL. In the DLL example, the LPTHREAD_START_ROUTINE uses a pointer to the address of LoadLibraryA (The winim module resolves this for you), followed by a pointer to the start of the DLL path string (rPtr) which is a parameter to the LoadLibraryA API call.

In closing, I’d like to thank a few people who published work on Nim or helped me when I had questions:

  • khchen’s winim module makes writing Nim code for Windows so much easier and more enjoyable by abstracting many of the details needed to make Windows API calls. If you struggle to figure out how to use winim to make Windows API calls (more complex than simple MsgBox stuff), be sure to check out kchen’s gists for examples!
  • @waldoirc Without their NimMusings example I’d probably still be debugging my shitty code. Thanks!
  • @byt3bl33d3r OffensiveNim GitHub repository. Byt3bl33d3r is definitely one of the most well-known pioneers on Offensive Nim.
  • @mttaggart has a Twitch stream (episodes are also on YouTube) which has been a huge help to experience his thought process while learning Nim. He also streams about coding in Rust as well as Red and Blue Team security topics.
  • S3cur3Th1sSh1t has been helpful when I’ve had questions on Offensive Nim. S3cur3Th1sSh1t’s NimSyscallPacker (only available to GitHub sponsors) is some amazing work on Offensive Nim.