malware · exploit
Windows malware creation notes
I recently worked through Sektor7’s Red Team Operator - Malware Development. These are my notes from the 2020 course.
The course is excellent because the author will supply you with all the templates you need to get started, and show you how to combine them for various tasks. He also provides a VM with all of the tools installed, and for each template there is a compiler .bat file to show you any compiler specific flags for each job. It is really good value.
These notes are mainly for me; but it might give you an idea of what you’re in for if you do the course. (Maybe worth noting: you can do the course in an afternoon if you can carve out an uninterrupted block. The course won’t make you an immediate expert, that’s impossible in 4 hours, but it gives you the building blocks; it is not high level stuff - he’ll show you how to get things done and you can choose where to spend more time).
PE Format
The sections we’ll be most interested in are:
.text
.data
.rsrc
“PEBear” is the tool we used to inspect them, but dumpbin from Visual Studio can do the job. Those are conventional section names: .text usually holds code, .data writable initialized data, and .rsrc resources. The section headers and their flags tell you what is actually present; the name alone doesn’t enforce a purpose. See Microsoft’s PE format reference.
DLLs aren’t designed to create their own process. Instead, the loader calls DllMain to initialize the DLL in the context of the host process, and exported functions can then be invoked by the loader or caller. As a red team malware developer, you’ll be most interested in making sure you’ve buttoned up DllMain and have an exportable module in your DLL.
rundll32 can host DLL entry points written for its calling convention. It isn’t a general way to call any exported function; Microsoft’s rundll32 documentation makes that distinction.
Also worth noting, we can dump the exportable functions from a dll with: dumpbin /exports MyDll.dll
Payloads and Storage
Using .text section
- Create a new memory buffer
- Copy the code to the buffer
- Execute
- We use local variable; will be on the stack
- Memory permissions are part of the behaviour an analyst can inspect. Writable/executable pages are interesting, but a permission choice alone doesn’t tell you whether a security product will detect the program.
Using .data section
- Similar to .text approach but global variable; not on the stack
Using .rsrc section
- Slightly more complicated.
- We produce a binary payload (msfvenom is the simple example)
- We name it as a resource for the new binary, like favicon.ico
- We import the payload in the dropper code; the template shows the right approach
Encoding and Encryption
Most of the simple approaches where we just stash a payload in a binary or dropper will be picked up by AV and analysts pretty quickly. We probably should encode at the very least, but really encrypt.
You can set up your encoded payload with built in tools like certutil.exe. You’d do:
certutil -encode calc.bin calc.b64
The templates for the course has decoding functions built in if you do go the simple encoding route.
The course compares XOR and AES examples. In this setting, the program needs to recover the payload itself, so the relevant key or decoding logic is available to an analyst too. Encoding and encryption change how the bytes look on disk; they don’t make the program’s behaviour disappear.
Call Obfuscation
getmodulehandle();
getprocaddress();
More information:
The functions used by a process are most often available in the import address table. AV can look and see that the usual suspects for malware are in the table and flag based on that. The idea of the getmodulehandle and getprocaddress approach is that we don’t have them mapped at the start, they are not in the import address table. The challenge though, is that the function names are still in the code and would still show up with strings.exe or similar tools - so AV has no trouble picking them out either. For that, we use the XOR approach to obfuscate the names in code. (Use the templates).
Backdooring PE
This was largely the same approach as shown in the OSCE but it was great to see it working in a couple of practical examples. Same options: Code Cave, New Section or Extended Section. Then remember:
- JMP -> to cave
- PUSHAD -> to save registers on the stack
- PUSHFD -> to save flags on stack
- {code, possibly decryption first}
- POPFD -> restore flags
- POPAD -> restore registers
- {overwritten instructions from the JMP}
- JMP -> to next instruction back at the start.
Code Injection
Getting our code into a different process. Few reasons, often to find a more stable process, or one the user won’t close; sometimes just to get our second connection.
Traditional approach (overview) is:
- Create memory for the code somewhere the target process has access
- Copy the code over
- Start a thread
Common API’s:
VirtualAllocEx();
WriteProcessMemory();
CreateRemoteThread();
References:
The .dll injection approach is a little different:
- Key difference is that the memory buffer in the destination is to hold a path to the .dll, rather than the code. Then we use LoadLibrary() and CreateRemoteThread(). We do it like CreateRemoteThread(LoadLibrary(PATHBUFFER)). It is in the templates.
Invisible Shellcode
Often folk make the mistake of leaving the shellcode dropper as a console app because it is easy. The challenge is that users will probably see a quick black screen flash when it executes which might spook them, especially if there was some interaction. If you instead use a GUI type (without drawing the GUI) you’ll have a little more stealth. Changes:
- main() becomes winmain()
- remove all printf() from any templates
- compile as GUI using /SUBSYSTEM:Windows
Bypassing Defender
Defender is going to be cross about this combo:
- VirtualAllocEx()
- WriteProcessMemory()
- CreateRemoteThread()
The course demonstrated these combinations against the Defender version available at the time. That result isn’t a promise that obscuring API names bypasses Defender. Static strings are only one part of what a security product can inspect.