Here I share notes on what I’m learning form Maldev Academy. This place is (at the moment) a mess of notes from various notetaking applications and approaches. Will consolidate and structure soon.
Module 3 - Required Tools
x64dbg
Some notes on the x64dbg interface.
The main tab (‘CPU’) has 4 screens:
- Disassembly (top-left): displays assembly instructions being executed
- Dump (bottom-left): displays memory contents
- Registers (top-right): displays values of CPU registers
- Stack (bottom-right): displays stack contents
Module 4 - Coding Basics
Structures/structs are declared using typedef to give aliases. F.e.:
typedef struct _STRUCTURE_NAME {
// structure elements
} STRUCTURE_NAME, *PSTRUCTURE_NAME;STRUCTURE_NAMEalias: refers to structure namePSTRUCTURE_NAMEalias: represents pointer to structure (P is a Microsoft convention)
Passing By Reference
Passing by reference is a method of passing arguments to a function where the argument is a pointer to the object, rather than a copy of the object’s value.
Module 5 - Windows Architecture

- User Processes - A program/application executed by the user such as Notepad, Google Chrome or Microsoft Word.
- Subsystem DLLs - DLLs that contain API functions that are called by user processes. An example of this would be
kernel32.dllexporting the CreateFile Windows API (WinAPI) function, other common subsystem DLLs arentldll.dll,advapi32.dllanduser32.dll. - Ntdll.dll - A system-wide DLL which is the lowest layer available in user mode. This is a special DLL that creates the transition from user mode to kernel mode. This is often referred to as the Native API or NTAPI.
- Executive Kernel - This is what is known as the Windows Kernel and it calls other drivers and modules available within kernel mode to complete tasks. The Windows kernel is partially stored in a file called
ntoskrnl.exeunder “C:\Windows\System32”.
Function Call Flow
The image below shows an example of an application that creates a file. It begins with the user application calling the CreateFile WinAPI function which is available in kernel32.dll. Kernel32.dll is a critical DLL that exposes applications to the WinAPI and is therefore can be seen loaded by most applications. Next, CreateFile calls its equivalent NTAPI function, NtCreateFile, which is provided through ntdll.dll. Ntdll.dll then executes an assembly sysenter (x86) or syscall (x64) instruction, which transfers execution to kernel mode. The kernel NtCreateFile function is then used which calls kernel drivers and modules to perform the requested task.

Module 6 - Windows Memory Management
Writing to memory example

Freeing allocated memory

Module 7 - Introduction to the Windows API
Windows Data Types
| Data type | function |
|---|---|
| DWORD | represents values up to 2^32-1 (32-bit uint) |
| size_t | represents size of an object (32- or 64-bit uint) |
| VOID | indicates absence of specific data type |
| PVOID | pointer of any data type (32-bit or 64-bit pointer) |
| HANDLE | specifies a particular object that the operating system is managing (file, process, thread) |
| HMODULE | handle to a module (DLL, EXE). base address of module in memory |
| LPCSTR/PCSTR | pointer to a constant null-terminated string of 8-bit Windows characters (ANSI). equivalent to const char* |
| PWSTR/LPWSTR | same as LPCSTR/PCSTR but does not point to a constant variable but to a readable and writable string (equivalent to wchar*) |
| wchar_t | same as wchar which is used to represent wide characters |
| ULONG_PTR | represents uint that is same size as pointer (32-bit or 64-bit). Used with arithmetic expressions containing pointers. |
| Data type | example |
|---|---|
| DWORD | DWORD dwVariable = 42; |
| size_t | SIZE_T = sVariable = sizeof(int); |
| VOID | void* pVariable = NULL; // This is the same as PVOID |
| PVOID | PVOID pVariable = &SomeData; |
| HANDLE | HANDLE hFile = CreateFile(…); |
| HMODULE | HMODULE hModule = GetModuleHandle(…); |
| LPCSTR/PCSTR | LCPSTR lpcString = ‘Hello, world!’ PCSTR pcwString = L’Hello, world!’ |
| PWSTR/LPWSTR | LPWSTR lpwString = L’Hello, world!’ PWSTR pwString = L’Hello, world!‘ |
| wchar_t | wchar_t wChar = L’A’; |
| ULONG_PTR | PVOID Pointer = malloc(100); //Pointer = pointer +10; //not allowed Pointer = (ULONG_PTR)Pointer + 10; // allowed |
Ansi & Unicode Functions
- CreateFileA ← ‘A’ indicates ‘ANSI’
- first parameter for
CreateFileAis aLPCSTR
- first parameter for
- CreateFileW ← ‘W’ indicates ‘Wide’ and represents Unicode
- first parameter for
CreateFileWisLPCWSTR
- first parameter for
Tip
To run single-file or sample code without a VS Studio project:
- Open the ‘Developer Command Prompt for VS 2022’
- cd to the source code and output folder (or specify the output folder with the flags
/Fe<output-dir>andFo<output-dir>)cl.exe single-file-or-sample-code.csingle-file-or-sample-code.exe
Windows API debugging errors
Learning to use the GetLastError function
#include <windows.h>
void ErrorExit()
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL) == 0) {
MessageBox(NULL, TEXT("FormatMessage failed"), TEXT("Error"), MB_OK);
ExitProcess(dw);
}
MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
ExitProcess(dw);
}
void main()
{
// Generate an error
if (!GetProcessId(NULL))
ErrorExit();
}
Now, to explain how this code does what it does:
| Function | Documentation | Application |
|---|---|---|
| FormatMessage() | Requires a message definition as input and formats a message string | If FormatMessage() receives an incorrect dw (dwMessageId in the function definition), MessageBox() ‘FormatMessage failed’ |
Module 8
Notepad++
Combining Notepad++ and the Visual Studio Developer Prompt Command Tools I have been able to compile sample/single file code, which is nice for this part of the course.

Build & run
In the future, I would like to incorporate the ‘build and run’ workflow into Notepad++, which is possible with NppExec and the following script:
// 1. Save the current file
NPP_SAVE
// 2. Move to the directory of the current file
CD $(CURRENT_DIRECTORY)
// 3. Setup VS environment, compile, and run
// /EHsc enables standard C++ exception handling
// /Fe: creates an output executable named after your file
cmd /c ""C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" && cl.exe /EHsc "$(FILE_NAME)" /Fe:"$(NAME_PART).exe" && "$(NAME_PART).exe""
PE Structure

DOS header data structure
typedef struct _IMAGE_DOS_HEADER {
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crcl; // Relocations
WORD e_cparhdr; //Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // Offset to the NT header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;The most important members of the struct are e_magic and e_lfanew.
e_magic is 2 bytes with a fixed value of 0x5A4D or MZ.
e_lfanew is a 4-byte value that holds an offset to the start of the NT Header. Note that e_lfanew is always located at an offset of 0x3C.
NT Header (IMAGE_NT_HEADERS)
NT Header contains FileHeader and OptionalHeader. NT Header can be reached via the e_lfanew member inside the DOS Header.
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32/64 OptionalHeader;
} IMAGE_NT_HEADERS32/64, *PIMAGE_NT_HEADERS32/64;File Header (IMAGE_FILE_HEADER)
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberofSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_OF_FILE_HEADER;NumberOfSections = number of sections in the PE file.
Characteristics = Flags that specify certain attributes about the executable file, such as whether it’s a dynamic-link library (DLL) or a console application.
SizeOfOptionalHeader = The size of the following optional header.
Optional header (IMAGE_OPTIONAL_HEADER)
32-bit-version:
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;64-bit version
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;Important struct members of _IMAGE_OPTIONAL_HEADER
Magic - Describes the state of the image file (32- or 64-bit image)
MajorOperatingSystemVersion - The major version number of the required operating system
MinorOperatingSystemVersion - The minor version number of the required operating system
SizeOfCode - The size of the .text section
AddressOfEntryPoint - Offset to the entry point of the file (Typically the main function)
BaseOfCode - Offset to the start of the .text section
SizeOfImage - The size of the image file in bytes
ImageBase - It specifies the preferred address at which the application is to be loaded into memory when it is executed.
DataDirectory - Array of IMAGE_DATA_DIRECTORY which contains the directories in a PE files
Data Directory
Array of data type IMAGE_DATA_DIRECTORY with the following data structure (below). Data Directory array is of size IMAGE_NUMBEROF_DIRECTORY_ENTRIES, constant value of 16.
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;Each element of the array represents a data directory containing data about a PE section or Data Table. SPecific data directories can be indexed:
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptorExport Directory
Contains addresses of exported functions and variables which can be used by other executable files to access the functions and data. Generally found in DLLs that export functions (like kernel32.dll exporting CreateFileA).
Import Address Table
Contains information about the addresses of functions imported from other executable files. Addresses are used to access the functions and data in other executables (like Application.exe importing CreateFileA from kernel32.dll.)
PE Sections
contains code and data used to create an executable program. PE secions have a unique name and contains (1) executable code, (2) data, or (3) resource information. Sections can be added later manually, the IMAGE_FILE_HEADER.NumberOfSections determine that number.
Important PE sections:
.text - executable code which is the written code
.data - initialized data which are variables initialized in the code
.rdata - read-only data which are constant variables prefixed with const
.idata - contains import tables which relate to functions called using the code
.reloc - information how to fix up memory addresses so the program can be loaded
.rsrc - store resources like icons and bitmaps
Each PE section has an IMAGE_SECTION_HEADER data structure, saved under the NT headers and are stacked above each other, representing a section. IMAGE_SECTION_HEADERcontains:
typedef STRUCT _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // name of the section (.text, .data, .rdata)
union {
DWORD PhysicalAddress; // size of the section in memory
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress; // offset of start of section in memory
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;'NT_STATUS' errors:
I haven’t been able to get rid of the error ‘unresolved external symbol NT_ERROR referenced in function …’ until I manually defined NT_ERROR. Specifying
/link kernel32.liband#include <ntdef.h>or<ntstatus.h>haven’t worked as well. Moving on for now.
Objectives
Open any EXE file with PE-Bear, click ‘DOS Header’ and locate the bytes 0x4D and 0x5A

Find the value of ‘e_lfanew’ (Hint: Look for the offset 0x3C)

Under the ‘File Hdr’ tab in PE-Bear, look at the value of ‘Sections Count’. Verify that number with the number of sections in the ‘Sections Hdrs’ tab.
‘File Hdr’ → ‘Sections Count’: 6 ‘Sections Hdrs’:

View the imported DLLs and Windows APIs under the ‘Imports’ tab

Module 9 - Dynamic-Link Library
Unlike EXE files, DLL files cannot execute code on their own: DLL libraries need to be invoked by other programs to execute the code.
Automatically loaded DLLs:
ntdll.dllkernel32.dllkernelbase.dll
Windows uses a system-wide DLL base address to load DLLs at the same base address in the virtual address space of all space of all running processes (f.e. kernel32.dll).
- There are 4 possibilities for a DLL entry point to be called:
DLL_PROCESS_ATTACH- A process is loading the DLL.DLL_THREAD_ATTACH- A process is creating a new thread.DLL_THREAD_DETACH- A thread exits normally.DLL_PROCESS_DETACH- A process unloads the DLL.
Code & findings
- Try to load a DLL dynamically (link to tutorial) maldevacademy socc studie
Mental note:
You can compile a DLL with
cl.exe /LD, optionally using user32.lib as linker input, like so:cl.exe /LD .\<DLL-code>.c user32.lib
- [-] Try to build and load the example DLL and accompanying exe.
Maldev Academy sampleDLL and exe invoking the HelloWorld function
sampleDLL.dll
////// sampleDLL.dll //////
#include <Windows.h>
// Exported function
extern __declspec(dllexport) void HelloWorld(){
MessageBoxA(NULL, "Hello, World!", "DLL Message", MB_ICONINFORMATION);
}
// Entry point for the DLL
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}This works:

sample exe
#include <windows.h>
// Constructing a new data type that represents HelloWorld's function pointer
typedef void (WINAPI* HelloWorldFunctionPointer)();
void call() {
// Attempt to get the handle of the DLL
HMODULE hModule = GetModuleHandleA("sampleDLL.dll");
if (hModule == NULL) {
// If the DLL is not loaded in memory, use LoadLibrary to load it
hModule = LoadLibraryA("sampleDLL.dll");
}
// pHelloWorld stores HelloWorld's function address
PVOID pHelloWorld = GetProcAddress(hModule, "HelloWorld");
// Typecasting pHelloWorld to be of type HelloWorldFunctionPointer
HelloWorldFunctionPointer HelloWorld = (HelloWorldFunctionPointer)pHelloWorld;
// Invoke HelloWorld
HelloWorld();
}
int main(){
call(); // Invoke the call() function
return 0;
}:
The example
sample.DLLand accompanying exe do compile, but the exported DLL functionHelloWorlddoesn’t work (also not withrundll32.exe) and while the library is correctly loaded, the pointer to the function is empty when the exe is ran.
:
For some reason, the code above does work when I compile both the DLL and the exe with cl (
cl.exe /LD DLL-code.c userlib32.dllandcl.exe exe-code.c).

SampleDLL.cpp and SampleApp.cpp from Microsoft documentation
Code for sample exe
// SampleApp.cpp
//
#include "stdafx.h"
#include "sampleDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HelloWorld();
return 0;
}Code for sample DLL
Code for sample DLL:
// SampleDLL.cpp
//
#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
)
{
return TRUE;
}
void HelloWorld()
{
MessageBox( NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}
// File: SampleDLL.h
//
#ifndef INDLL_H
#define INDLL_H
#ifdef EXPORTING_DLL
extern __declspec(dllexport) void HelloWorld();
#else
extern __declspec(dllimport) void HelloWorld();
#endif
#endifObjectives
Todo
Do the objectives below.
Review the 4 entry points for a DLL
Read the documentation on LoadLibrary, GetModuleHandle and GetProcAddress
Create a DLL that exports a function
See the DLL and exe code here.
See the DLL and exe in action below.


Invoke the function using Rundll32.exe

Create an EXE program that loads a DLL. Verify the DLL was loaded using Process Hacker

Module 10
Diagram of how EDRs hook into API calls : any function names used by the portable executable at runtime or the libraries (DLLs) that export these functions listed in the IAT can help identify what WinAPIs the executable is using.
Objectives
Familiarize yourself with how YARA rules work to detect malware
Example YARA rule from https://yara.readthedocs.io/en/stable/writingrules.html:
rule ExampleRule
{
strings:
$my_text_string = "text here"
$my_hex_string = { E2 34 A1 C8 23 FB }
condition:
$my_text_string or $my_hex_string
}Some more notes from this chapter of the documentation:
” Rules are generally composed of two sections: strings definition and condition.
The strings definition section can be omitted if the rule doesn’t rely on any string, but the condition section is always required. The strings definition section is where the strings that will be part of the rule are defined. Each string has an identifier consisting of a $ character followed by a sequence of alphanumeric characters and underscores, these identifiers can be used in the condition section to refer to the corresponding string. Strings can be defined in text or hexadecimal.(…) Text strings are enclosed in double quotes just like in the C language. Hex strings are enclosed by curly brackets, and they are composed by a sequence of hexadecimal numbers that can appear contiguously or separated by spaces. Decimal numbers are not allowed in hex strings.
The condition section is where the logic of the rule resides. This section must contain a boolean expression telling under which circumstances a file or process satisfies the rule or not. Generally, the condition will refer to previously defined strings by using their identifiers. In this context the string identifier acts as a boolean variable which evaluate to true if the string was found in the file or process memory, or false if otherwise. ”
Other important aspects of rules: ” Global rules give you the possibility of imposing restrictions in all your rules at once. (…) they will be evaluated before the rest of the rules, which in turn will be evaluated only if all global rules are satisifed. Private rules are (…) not reported by YARA when they match on a give file. Rule tags (…) can be used later to filter YARA’s output and show only the rules that you are interested in. “
Search the following file hash on VirusTotal: e8ac867e5f51bdcf5ab7b06a8bced131

Click the ‘Behavior’ tab on the file’s results in VirusTotal. What suspicious/malicious behavior does this file perform?
Below are some aspects of suspicious/malicious behavior this file performs.
Info
VirusTotal categorizes malware behavior in the ‘Behavior’ tab not with Mitre Att&ck-framework taxonomy (f.e. T1659 for ‘Content Injection’) but instead an unfamiliar ‘OB0001’ for ‘Anti-Behavior Analysis. These codes link back to the ‘MBC Project’ or ‘Malware Behavior Catalog (link to Github-repo. About the relationship ATT&CK-MBC: “As a publicly available framework, The Malware Behavior Catalog (MBC) aims to directly and explicitly define malware behaviors and code characteristics to support malware analysis-oriented use cases. MBC defines behaviors outside ATT&CK’s scope and enhances some ATT&CK techniques and sub-techniques to be malware-focused.”
- Anti-Behavioral Analysis (OB0001)
- Debugger Detection (B0001)
- Collection (OB0003)
- Keylogging (F0002)
- Impact (OB0006)
- Denial of Service (B0033)
- Cryptography (OC0005)
- Encrypt Data (C0027)
And many more, see the page here.
Click the ‘Details’ tab and scroll down to ‘Imports’. What DLLs are imported? What WinAPIs are imported from User32.dll?
- KERNEL32.dll
- USER32.dll
- AdjustWindowRectEx
- AppendMenuA
- ArrangeIconicWindows
- BeginDeferWindowPos
- BeginPaint
- BringWindowToTop
- CallNextHookEx
- CallWindowProcA
- ChangeClipboardChain
- CharNextA
- GDI32.dll
- COMDLG32.dll
- WINSPOOL.DRV
- ADVAPI32.dll
- SHELL32.dll
- COMCTL32.dll
- SHLWAPI.dll
- oledlg.dll
- ole32.dll
- OLEAUT32.dll
Module 11
Virtual memory allows the operating system to use more memory than what is physically available by creating a virtual address space that can be accessed by the applications. These virtual address spaces are divided into ‘pages’ which are then allocated to processes.
| Type of memory | Definition |
|---|---|
| Private memory | dedicated to a single process, stores specific data |
| Mapped memory | can be shared, stores shared libraries, memory segments, shared files |
| Image memory | contains code and data of executable, often related to DLL files loaded into process address space |
Process Environment Block (PEB)
PEB is a Windows data structure containing process information like:
- parameters
- startup information
- allocated head information
- loaded DLLs
- process ID (PID) and path to executable
PEB struct in C reserved members (can be ignored)):
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoute;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;PEB struct reserved members:
-
BeingDebugged- indicates whether the process is being debugged, used by the Windows loader
-
Ldr- pointer to
PEB_LDR_DATAstruct containing information about process’s loaded DLLs - can be leveraged to find base address of particular DLL and functions
- used for a custom version of
GetModuleHandleA/Wfor added stealth
- pointer to
typedef struct _PEB_LDR_DATA {
BYTE Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;ProcessParameters- data structure containing command line parameters passed to process
- used for command line spoofing
typedef struct _RTL_USER_PROCESS_PARAMETERS {
BYTE Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;-
AtlTHunkSListPtrandAtlThunkSListPtr32- used by the ATL (Active Template Library) to store a pointer to linked list of thunking functions
- thunking functions used to call functions implemented in different address space
- often represent functions exported from a DLL
- used by the ATL (Active Template Library) to store a pointer to linked list of thunking functions
-
PostProcessInitRoutine
- stores pointer to function called by OS after TLS (Thread Local Storage) initialization
-
SessionId
- unique identifier assigned to single session
Thread Environment Block (TEB)
typedef struct _TEB {
PVOID Reserved1[12];
PPEB ProcessEnvironmentBlock;
PVOID Reserved2[399];
BYTE Reserved3[1952];
PVOID TlsSlots[64];
BYTE Reserved4[8];
PVOID Reserved5[26];
PVOID ReservedForOle;
PVOID Reserved6[4];
PVOID TlsExpansionSlots;
} TEB, *PTEB;ProcessEnvironmentBlock (PEB) is a pointer to the PEB structure, located inside the Thread Environment Block (TEB) and used to store information about the currently running process.
TlsSlots (Thread Local Storage) Slots are locations in the TEB used to store thread-specific data (thread-specific variables, handles, states).
TlsExpansionSlots are a set of pointers to store thead-local storage data for a thread, reserved for use by system DLLs.
Objectives
Open Process Hacker and double click on a process, select the ‘Threads’ tab and look at the number of threads running

Click the ‘Memory’ tab and view the different memory regions

Read Microsoft’s documentation for OpenProcess and OpenThread
OpenProcess
” OpenProcess
Function (processthreadsapi.h) Opens an existing local process object. ”
HANDLE OpenProcess(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId
);Parameters
[in] dwDesiredAccess
The access to the process object. This access right is checked against the security descriptor for the process. This parameter can be one or more of the process access rights.
If the caller has enabled the SeDebugPrivilege privilege, the requested access is granted regardless of the contents of the security descriptor.
[in] bInheritHandle
If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do not inherit this handle.
[in] dwProcessId
The identifier of the local process to be opened.
If the specified process is the System Idle Process (0x00000000), the function fails and the last error code is ERROR_INVALID_PARAMETER. If the specified process is the System process or one of the Client Server Run-Time Subsystem (CSRSS) processes, this function fails and the last error code is ERROR_ACCESS_DENIED because their access restrictions prevent user-level code from opening them.
If you are using GetCurrentProcessId as an argument to this function, consider using GetCurrentProcess instead of OpenProcess, for improved performance.
Return value If the function succeeds, the return value is an open handle to the specified process. If the function fails, the return value is NULL. To get extended error information, call GetLastError.
Optional: Run notepad.exe and then use OpenProcess to get a handle to the process (Hint: copy notepad’s PID from Process Hacker)

To print a handle in C using printf(), you typically convert the handle to a pointer type and use the %p format specifier.
For example, if you have a handle defined as HANDLE h = …;, you would write printf(“Handle: %p\n”, (void*)h); to display it.

#include <Windows.h>
#include <stdio.h>
HANDLE hProcess;
DWORD pid = 14844;
void main() {
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
printf("The handle is: %p", pid);
}Review the different memory types
To reiterate, this module names three types of memory for processes:
-
Private memory → dedicated to a single process.
-
Mapped memory → shared libraries, shared memory segments, and shared files. Mapped memory is visible to other processes, but is protected from being modified by other processes. Source ” A memory-mapped file contains the contents of a file in virtual memory. This mapping between a file and memory space enables an application, including multiple processes, to modify the file by reading and writing directly to the memory. (…) There are two types of memory-mapped files: - Persisted memory-mapped files Persisted files (…) are associated with a source file on a disk. When the last process has finished working with the file, the data is saved to the source file on the disk. These memory-mapped files are suitable for working with extremely large source files. - Non-persisted memory-mapped files Non-persisted files (…) are not associated with a file on a disk. When the last process has finished working with the file, the data is lost and the file is reclaimed by garbage collection. These files are suitable for creating shared memory for inter-process communications (IPC). _To work with a memory-mapped file, you must create a view of the entire memory-mapped file or a part of it. (…) Use stream access views for sequential access to a file; this is recommended for non-persisted files and IPC. Random access views are preferred for working with persisted files. _ (…) ” This documentation page also features some example code (unfortunately C#, which I’m unfamiliar with). Some notes from this code: “Persisted Memory-Mapped Files”
-
create a memory-mapped file: ”_(…) `var mmf = MemoryMappedFile.CreateFromFile(@“c:\ExtremelyLargeImage.data”, FileMode.Open,“ImgA”) _”
-
create a random access view from a 256 megabytes offset to the 768th megabyte (the offset plus a 512 megabyte length):
var accessor = mmf.CreateViewAccessor(offset, length) -
Image memory → contains the code and data of an executable file, DLL files loaded into address space.
Review the PEB and TEB structures
Module 12 - Undocumented Structures
reserved members within stuctures are presented as arrays of BYTES or PVOID data types and prevent users from understanding the structure to avoid modifications to these reserved members.
One way to determine PEB’s reserved members is through the !peb command in WinDbg.
Choosing a structure definition
- Some structure definitions only work for a specific architecture (x86 or x64 f.e.)
- It can be necessary to define multiple structures due to nested structures (PEB contains a member that acts as a pointer to another structure)
- Select only one definition of a structure to avoid redefinition errors thrown by a compiler (f.e. Visual Studio’s)
Module 13 - Payload placement - .data & .rdata sections
The .data and .rdata sections can be merged / merged into the .text section.
.data section
benefit of string payload in #.data section: readable and writable, so you can have an encrypted payload that is decrypted at runtime
code snippet example of payload stored in .data section:
#include <Windows.h>
#include <stdio.h>
// msfvenomn calc shellcode generated with:
// msfvenomn -p windows/x64/exec CMD=calc.exe -f c
// .data saved payload
unsigned char Data_RawData[] = {
// hex characters
};
int main() {
printf("[i] Data_RawData var : 0x%p \n", Data_RawData);
printf("[#] Press <Enter> To Quit ...");
getchar();
return 0;
}
.rdata section
The ‘r’ in .rdata indicates read-only, which can be done using the const (constant) qualifier for variables.
An example of this using the previous code:
#include <Windows.h>
#include <stdio.h>
// msfvenom calc shellcode
// msfvenom -p windows/x64/exec CMD=calc.exe -f c
// .rdata saved payload
const unsigned char Rdata_RawData[] = {
// hex characters
};
int main() {
printf("[i] Rdata_RawData var: 0x%p\n", Rdata_RawData);
printf("[#] Press <Enter> To Quit...");
getchar();
return 0;
}.text section
The #.text section stores variables within executable memory permissions so they can be executed directly without editing the memory regions permissions. Mostly useful for small payloads, less than 10 bytes. Saving a variable in the .text section requires you to instruct the compiler to do so. An example:
#include <Windows.h.>
#include <stdio.h>
// mfsvenom calc shellcode
// msfvenom -p windows/x64/exec CMD=calc.exe -f c
// .text saved payload
#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Text_RawData[] = {
// hex characters
};
int main() {
printf("[i] Text_RawData var: 0x%p\n", Text_RawData);
printf("[#] Press <Enter> To Quit...");
getchar();
return 0;
}.rsrc section
Larger payloads cannot be stored in the .data or .rdata sections due to size limits. The #.rsrc section is cleaner (no errors during compilation).
One way to store a payload in the .rsrc section with Visual Studio:
- right-click ‘Resource files’ ⇒ ‘Add’ ⇒ ‘New Item…’
- ‘Resource File’
- Right-click on the ‘Resource.rc’ default file ⇒ select ‘Add Resource’
- ‘Import’
- Select
calc.icowhich is raw payload - In prompt, enter ‘RCDATA’ under ‘Resource Type:’
resource.hshould be visible and named according to the previous.rcfile
Payloads stored in .rsrc section cannot be accessed directly but through these WINAPIs:
FindResourceW- get location of specified data stored in resource section of a special ID passed in (this is defined in the header file)LoadResource- retrieves aHGLOBALhandle of resource data, which can be used to obtain the base address of the specified resource in memoryLockResource- obtains pointer to specified data in resource section from its handleSizeofResource- gets size specified data in resource section
#include <Windows.h>
#include <stdio.h>
#include "resource.h"
int main() {
HRSRC hRsrc = NULL;
HGLOBAL hGlobal = NULL;
PVOID pPayloadAddress = NULL;
SIZE_T sPayloadSize = NULL;
// Get location to data stored in .rsrc by its id *IDR_RCDATA1*
hRsrc = FindResourceW(NULL, MAKEINTRESOURCEw(IDR_RCDATA1), RT_RCDATA);
if (hRsrc == NULL) {
// in case of function failure
printf("[!] FindResourceW failed with error: %d \n", GetLastError());
- [ ] return -1;
}
// GetHGLOBAL or the handle of the specified resource data since its required to call LockResource later on
hGlobal = LoadResource(NULL, hRsrc);
if (hGlobal == NULL) {
// in case of function failure
printf("[!] LoadResource failed with errror: %d \n", GetLastError());
return -1;
}
// Get the address of our payload in .rsrc section
pPayloadAddress = LockResource(hGlobal);
if (pPayloadAddress == NULL) {
// in case of function failure
printf("[!] LockResource failed with Error: %d \n", GetLastError());
return -1;
}
// Get the size of our payload in .rsrc section
sPayloadSize = SizeofResource(NULL, hRsrc);
if (sPayloadSize == NULL) {
// in case of function failure
printf("[!] SizeofResource failed with error: %d \n", GetLastError());
return -1;
}
// Printing pointer and size to the screen
printf("[i] pPayloadAddress var: 0x%p \n", pPayloadAddress);
printf("[i] sPayloadSize var: %ld \n", sPayloadSize);
printf("[#] Press <Enter> to quit...");
getchar();
return 0;
}Important to note: the payload address that this code prints to the screen, is read-only memory. To edit a payload, you must allocate a buffer with the same size as the payload and copy it over. The new buffer is where changes (like decrypting a payload!) can be made. This can be done with memcpy like this:
// Allocating memory using a HeapAlloc call.
PVOID pTmpBuffer = HeapAlloc(GetProcessHeap(), 0, sPayloadSize);
if (pTmpBuffer != NULL){
// copying the payload from the resource section to the new buffer.
memcpy(pTmpBuffer, pPayloadAddress, sPayloadSize);
}
// Printing the base address of our buffer (pTmpBuffer)
printf("[i] pTmpBuffer var: 0x%p \n", pTmpBuffer);Module 16 - Introduction to Payload Encryption
Encrypting (parts of) the malware is almost always necessary against modern security solutions. Encryption may not be effective against f.e. runtime-analysis and heuristic-analysis. The more data that’s encrypted within a file, the higher the entropy of the file. The most widely used encryption algorithms in malware development are:
- XOR
- AES
- RC4
Payload Encryption - XOR
Exclusive or or XOR encryption is faster than AEC and RC4 and does not require additional libraries or the Windows API. It is also a bidirectional encryption algorithm.
Example of an XOR encryption function:
/*
- pShellcode: Base address of the payload to encrypt.
- sShellcodeSize: Size of the payload.
- bKey: A single arbitrary byte representing the key for encrypting the payload.
*/
VOID XorByOneKey(IN PBYTE pShellcode, IN SIZE_T sShellcodeSize, IN BYTE bKey) {
for (size_t i = 0; i < sShellcodeSize; i++){
pShellcode[i] = pShellcode[i] ^ bKey;
}
}