Assembly Snippets for Windows
This page is a collection of functions implemented in Assembly.
- Retrieve the base address of kernel32.dll
- Retrieve the address of an exported function - GetProcAddress equivalent
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:
- Go to ‘Solution Explorer’
- Right click on ‘Source File’ -> Add -> New Item…
- At the bottom of the window that opens, type a name in the format ‘
.asm' and hit the 'Add' button - 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
- 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 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
tags: #assembly