Ythehacker
Member
Introduction
This article will delve into fundamental and techniques related to malware development for Windows OS, We’ll start by exploring the WinAPI, understanding how code manipulation works in the context of malware development. We’ll cover things like loading functions dynamically, accessing the Process Environment Block (PEB), and executing functions in code, Next we’ll look into obfuscation and payload encoding. We’ll use techniques like XOR and AES encryption to make our malicious code harder to detect. We’ll also explore ways to insert our malicious code, like using classic shellcode and DLL injections To wrap things up, we’ll create a simple rootkit, concluding our journey through the world of malware development, As usual, we will delve into the code and techniques, providing a detailed, step-by-step breakdown.
Dynamic Function Loading and Execution
Time to dive into some naked code action! We’re gonna break down this code and make it crystal clear, so you can get what’s going on.
This is a simple program. It uses the MessageBoxA function, which is part of the Windows API. This function displays a modal dialog box with specified text and a caption. In this code, we’re making a straightforward call to the MessageBoxA function to show a message box.
The MessageBoxA function is statically linked to your program during the compilation process. This means that the function’s code is included in your program, so you don’t need to load it at runtime.
Now, let’s contrast this with the following code:
In this code, we take a different approach. We dynamically load and call the MessageBoxA function using the GetProcAddress function. This function retrieves the address of the MessageBoxA function from the USER32.dll library at runtime.
To work with this dynamically loaded function, we define a function pointer type def_MessageBoxA that matches the signature of the MessageBoxA function. We then cast the obtained function address to this function pointer and use it to call the function.
So, how is this related to malware? Well, by dynamically loading functions, we can avoid having to statically link to libraries that are associated with shady activity, meaning that hooking a function dynamically with the use of pointers can make it more challenging for static analysis tools to identify the behavior of the code. Let’s Take an example:
In this example, we have a DLL with two exported functions, func01 and func02. Initially, both functions display message boxes. However, in the DllMain function, which is automatically executed when the DLL is loaded may start with benign functionality but could use dynamic function loading and function hooking to change the behavior of func01 at runtime. So, let’s continue on this code manipulation journey, exploring the intricacies of dynamic function loading, PEB access, and function execution, which are essential concepts in understanding how code can be adapted and manipulated.
Before, continuing I would like to highlight in which step PEB is created on process creation When Starting a program (calc.exe for example): calc.exe will call a win32 API function : CreateProcess which sends to the OS the request to create this process and start the execution.
Creating the process data structures: Windows creates the process structure EPROCESS on kernel land for the newly created calc.exe process, Initialize the virtual memory: Then, Windows creates the process, virtual memory, and its representation of the physical memory and saves it inside the EPROCESS structure, creates the PEB structure with all necessary information, and then loads the main two DLLs that Windows applications will always need, which are ntdll.dll and kernel32.dll and finally loading the PE file and start the execution.
the PEB is accessed to retrieve information about loaded modules, specifically the base addresses of dynamically linked libraries (DLLs). Let’s explore how the PEB is used in the code:
As you can see, the PEB is a robust structure. The code defines several structures, such as PEB32, PEB_LDR_DATA32, and LDR_DATA_TABLE_ENTRY32, which are simplified versions of the actual PEB data structures. These structures contain fields that hold information about loaded modules and their locations in memory.
The GetModHandle function accesses the PEB to find the base address of a loaded module. The PEB contains a data structure called PEB_LDR_DATA that manages information about loaded modules. The InMemoryOrderModuleList field of this structure is a linked list of loaded modules. The GetModHandle function iterates through this list and compares module names to find the desired module based on the libName parameter.
The PEB can be found at fs:[0x30] in the Thread Environment Block for x86 processes as well as at GS:[0x60] for x64 processes.
Next we call the GetFuncAddrfunction which well be used to locate the address of a specific function within a loaded module. It takes the moduleBase parameter, which is the base address of the module, and it looks into the export table of the module to find the address of the function with the specified name (szFuncName). The export table is part of the module’s data structure, which is managed by the PEB.
The function begins by parsing the export table of the loaded module to access information about its exported functions. The export table is part of the Portable Executable (PE) file format and contains details about functions that can be accessed externally.
"TO BE CONTINUED"
This article will delve into fundamental and techniques related to malware development for Windows OS, We’ll start by exploring the WinAPI, understanding how code manipulation works in the context of malware development. We’ll cover things like loading functions dynamically, accessing the Process Environment Block (PEB), and executing functions in code, Next we’ll look into obfuscation and payload encoding. We’ll use techniques like XOR and AES encryption to make our malicious code harder to detect. We’ll also explore ways to insert our malicious code, like using classic shellcode and DLL injections To wrap things up, we’ll create a simple rootkit, concluding our journey through the world of malware development, As usual, we will delve into the code and techniques, providing a detailed, step-by-step breakdown.
Dynamic Function Loading and Execution
Time to dive into some naked code action! We’re gonna break down this code and make it crystal clear, so you can get what’s going on.
int main(void) {
MessageBoxA(0, "Foo Here.", "info", 0);
return 0;
}
This is a simple program. It uses the MessageBoxA function, which is part of the Windows API. This function displays a modal dialog box with specified text and a caption. In this code, we’re making a straightforward call to the MessageBoxA function to show a message box.
The MessageBoxA function is statically linked to your program during the compilation process. This means that the function’s code is included in your program, so you don’t need to load it at runtime.
Now, let’s contrast this with the following code:
int main(void) {
size_t get_MessageBoxA = (size_t)GetProcAddress( LoadLibraryA("USER32.dll"), "MessageBoxA" );
def_MessageBoxA msgbox_a = (def_MessageBoxA) get_MessageBoxA;
msgbox_a(0, "Foo Here.", "info", 0);
return 0;
}
In this code, we take a different approach. We dynamically load and call the MessageBoxA function using the GetProcAddress function. This function retrieves the address of the MessageBoxA function from the USER32.dll library at runtime.
To work with this dynamically loaded function, we define a function pointer type def_MessageBoxA that matches the signature of the MessageBoxA function. We then cast the obtained function address to this function pointer and use it to call the function.
So, how is this related to malware? Well, by dynamically loading functions, we can avoid having to statically link to libraries that are associated with shady activity, meaning that hooking a function dynamically with the use of pointers can make it more challenging for static analysis tools to identify the behavior of the code. Let’s Take an example:
__declspec(dllexport) void func01() { MessageBoxA(0, "", "Function 1", 0); }
__declspec(dllexport) void func02() { MessageBoxA(0, "", "Function 2", 0); }
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
if (fdwReason == DLL_PROCESS_ATTACH) {
// Hook function func01
}
return TRUE;
}
In this example, we have a DLL with two exported functions, func01 and func02. Initially, both functions display message boxes. However, in the DllMain function, which is automatically executed when the DLL is loaded may start with benign functionality but could use dynamic function loading and function hooking to change the behavior of func01 at runtime. So, let’s continue on this code manipulation journey, exploring the intricacies of dynamic function loading, PEB access, and function execution, which are essential concepts in understanding how code can be adapted and manipulated.
Before, continuing I would like to highlight in which step PEB is created on process creation When Starting a program (calc.exe for example): calc.exe will call a win32 API function : CreateProcess which sends to the OS the request to create this process and start the execution.
Creating the process data structures: Windows creates the process structure EPROCESS on kernel land for the newly created calc.exe process, Initialize the virtual memory: Then, Windows creates the process, virtual memory, and its representation of the physical memory and saves it inside the EPROCESS structure, creates the PEB structure with all necessary information, and then loads the main two DLLs that Windows applications will always need, which are ntdll.dll and kernel32.dll and finally loading the PE file and start the execution.
- PEB can be accessed from User Mode - Contains Process specific information
- EPROCESS can be only be accessed from Kernel Mode
PEB Structure
PEB is a data structure in the Windows operating system that contains information and settings related to a running process, The process control block contains data that is only useful to the kernel, such as the preferred CPU for this process. The Thread Control Block is entirely different, and is what the kernel uses to manage threads, which are what the kernel runs at the lowest level.the PEB is accessed to retrieve information about loaded modules, specifically the base addresses of dynamically linked libraries (DLLs). Let’s explore how the PEB is used in the code:
typedef struct _PEB_LDR_DATA {
ULONG Length;
UCHAR Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID EntryInProgress;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
typedef struct _UNICODE_STRING32 {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING32, *PUNICODE_STRING32;
typedef struct _PEB32 {
// ...
} PEB32, *PPEB32;
typedef struct _PEB_LDR_DATA32 {
// ...
} PEB_LDR_DATA32, *PPEB_LDR_DATA32;
typedef struct _LDR_DATA_TABLE_ENTRY32 {
// ...
} LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32;
As you can see, the PEB is a robust structure. The code defines several structures, such as PEB32, PEB_LDR_DATA32, and LDR_DATA_TABLE_ENTRY32, which are simplified versions of the actual PEB data structures. These structures contain fields that hold information about loaded modules and their locations in memory.
size_t GetModHandle(wchar_t *libName) {
PEB32 *pPEB = (PEB32 *)__readfsdword(0x30); // ds: fs[0x30]
PLIST_ENTRY header = &(pPEB->Ldr->InMemoryOrderModuleList);
for (PLIST_ENTRY curr = header->Flink; curr != header; curr = curr->Flink) {
LDR_DATA_TABLE_ENTRY32 *data = CONTAINING_RECORD(
curr, LDR_DATA_TABLE_ENTRY32, InMemoryOrderLinks
);
printf("current node: %ls\n", data->BaseDllName.Buffer);
if (StrStrIW(libName, data->BaseDllName.Buffer))
return data->DllBase;
}
return 0;
}
The GetModHandle function accesses the PEB to find the base address of a loaded module. The PEB contains a data structure called PEB_LDR_DATA that manages information about loaded modules. The InMemoryOrderModuleList field of this structure is a linked list of loaded modules. The GetModHandle function iterates through this list and compares module names to find the desired module based on the libName parameter.
The PEB can be found at fs:[0x30] in the Thread Environment Block for x86 processes as well as at GS:[0x60] for x64 processes.
Next we call the GetFuncAddrfunction which well be used to locate the address of a specific function within a loaded module. It takes the moduleBase parameter, which is the base address of the module, and it looks into the export table of the module to find the address of the function with the specified name (szFuncName). The export table is part of the module’s data structure, which is managed by the PEB.
size_t GetFuncAddr(size_t moduleBase, char* szFuncName) {
// parse export table
PIMAGE_DOS_HEADER dosHdr = (PIMAGE_DOS_HEADER)(moduleBase);
PIMAGE_NT_HEADERS ntHdr = (PIMAGE_NT_HEADERS)(moduleBase + dosHdr->e_lfanew);
IMAGE_OPTIONAL_HEADER optHdr = ntHdr->OptionalHeader;
IMAGE_DATA_DIRECTORY dataDir_exportDir = optHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
// parse exported function info
PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)(moduleBase + dataDir_exportDir.VirtualAddress);
DWORD* arrFuncs = (DWORD *)(moduleBase + exportTable->AddressOfFunctions);
DWORD* arrNames = (DWORD *)(moduleBase + exportTable->AddressOfNames);
WORD* arrNameOrds = (WORD *)(moduleBase + exportTable->AddressOfNameOrdinals);
The function begins by parsing the export table of the loaded module to access information about its exported functions. The export table is part of the Portable Executable (PE) file format and contains details about functions that can be accessed externally.
- accesses the DOS header and the NT header to navigate to the Optional Header of the PE file.
- identifies the data directory for exports using the IMAGE_DIRECTORY_ENTRY_EXPORT index from the Optional Header’s data directory array.
- calculates the address of the export table, which holds data related to the module’s exported functions.
// lookup
for (size_t i = 0; i < exportTable->NumberOfNames; i++) {
char* sz_CurrApiName = (char *)(moduleBase + arrNames[i]);
WORD num_CurrApiOrdinal = arrNameOrds[i] + 1;
if (!stricmp(sz_CurrApiName, szFuncName)) {
printf("[+] Found ordinal %.4x - %s\n", num_CurrApiOrdinal, sz_CurrApiName); //enumeration process
return moduleBase + arrFuncs[ num_CurrApiOrdinal - 1 ];
}
}
return 0;
}
"TO BE CONTINUED"