Assembly Snippets for Windows

This page is a collection of functions implemented in Assembly.

Include Assembly code in a Visual Studio project

Some initial legwork is required to include the Assembly code in the Visual Studio project. After this, the process to call the function is straightforward.

First, create an empty C++ console application. The .asm file where the function is implemented has to be included in the solution. To do that:

  1. Go to ‘Solution Explorer’
  2. Right click on ‘Source File’ -> Add -> New Item…
  3. At the bottom of the window that opens, type a name in the format ‘.asm' and hit the 'Add' button
  4. Setup MASM (Microsoft Assmbler) by pressing right-click on the project on the Solution Explorer and then Build Dependencies -> Build Customization and select MASM on the box that appears on the screen
  5. As an extra step, to generate a listing file that will generate the actual Assembly opcodes, right-click on the project on the Solution Explorer windows, then click on Properties context menu. On the properties window that opens, click on Microsoft Macro Assembler -> Listing File -> Enable Assembly Generated Code Listing select Yes (/Sg). Then, on the same window, set the Assembled Code Listing File option to ($IntDir)$(ProjectName).lst

The file that hosts the Assembly code, should now be visible in the ‘Solution Explorer’ under ‘Source Files’.

An example function that returns the string that it receives as input should have the following format:

.code
	FunctionWrittenInAssembly proc
		xor rax, rax
		xchg rax, rcx
		ret
	FunctionWrittenInAssembly endp
end

The file that contains the main function - a .cpp file - has the following format:

#include <windows.h>

// define the function that is written in Assembly
extern "C" LPVOID FunctionWrittenInAssembly(CHAR*);

int main()
{
	CHAR input[] = "test input";
	// call the function that is written in Assembly
	CHAR* out = (CHAR*)FunctionWrittenInAssembly(input);
	printf(L"[+] output: %s\n", out);
}

Retrieve the base address of kernel32.dll

The following function walks the InMemoryOrderModuleList structure and assumes that kernel32 is the second module that is loaded in the process and returns the module’s address.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; function to retrieve the address of KERNEL32.DLL ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
GetKernel32Address proc
	mov rbx, gs:[60h]		; pointer to PEB
	mov rbx, [rbx + 18h]		; pointer to PEB_LDR_DATA
	mov rbx, [rbx + 10h]		; pointer to InMemoryOrderModuleList
	mov rbx, [rbx]			; pointer to ntdll.dll's _LDR_DATA_TABLE_ENTRY
	mov rbx, [rbx]			; pointer to kernel32.dll's _LDR_DATA_TABLE_ENTRY
	mov rbx, [ rbx + 30h]		; kernel32.dll base address
	xchg rax, rbx
	;mov rax, rbx			; either			
	ret
GetKernel32Address endp

Retrieve the address of an exported function - GetProcAddress equivalent

This is an equivalent of GetProcAddress with additional features. The function requires two arguments, the base address of the module where the function is located and the hashed API name of this function. The hash algorithm used to produce the hashes is the one The Last Stage of Delirium Research Group wrote. Have a look at the implementation of the algorithm on (this)[https://stmxcsr.com/2020/05/28/kernel32-api-hashes.html] link.

To call the function from main, assuming you are trying to retrieve VirtualAlloc, do the following:

// Assembly function from above
LPVOID kernel32 = GetKernerl32Address();
//0x48fa7604 is the hashed name of the VirtualAlloc
LPVOID address = AsmGetProcAddress(kernel32,0x48fa7604);
printf("[+] VirtualAlloc is located at: %p", address);

The actual implementation of the function in Assembly, is:

.code

	AsmGetProcAddress proc

		; rcx - input module base address
		; rdx - input hashed API name

		xchg r11, rdx

		; rax: later in use
		; rbx: RVA export table
		; rcx: module base address
		; rdx: RVA of export table
		; r8: AddressOfName
		; r9: NumberOfNames

	function_lookup:
		;input: rcx - base address of the module
		mov ebx, [rcx + 3ch]	; offset to PE signature 
		add rbx, 88h			; Export Table (RVA)
		add rbx, rcx			; Export Table (VA)
		mov edx, [rbx]			; RVA of export table 
		mov rbx, rdx
		add rbx, rcx
		mov edx, [rbx + 20h]	; AddresOfNames RVA
		mov r9d, [rbx + 18h]	; NumberOfNames
		mov r8, rcx
		add r8, rdx				; AddressOfName VA

	api_va:
		test r9d, r9d			; check if NumberOfNames is not zero
		jz exit					; if zero exit
		dec r9
		mov r14d, [r8 + r9 * 4]	; AddressOfName + NumberOfNames
		add r14, rcx			; VA of the API

	; hash the name of the API
	hash_api:
		lea rsi, [r14]
		lodsb
		xor r13, r13			; hashed API
	hash_loop:
		mov r12d, r13d
		shl r13d, 5
		shr r12d, 27
		or r13d, r12d
		add r13d, eax
		lodsb
		test al, al
		jnz hash_loop

	; compare the provided hashed API name with the one found
	compare_hash:
		cmp r13d, r11d
		jnz api_va

	find_addr:
		mov r8d, [rbx + 24h]
		add r8, rcx
		xor rax, rax
		mov ax, [r8 + r9 * 2]
		mov r8d, [rbx + 1ch]
		add r8, rcx
		mov eax, [r8 + rax * 4]	; function VMA
		add rax, rcx

	exit:
		ret
	AsmGetProcAddress endp
end