[0x01] - General Code Structure
What we’re doing:
How we’re doing it:
We’ll start by declaring the only two imports we require to bootstrap our loader.
; HMODULE GetModuleHandleA(LPCSTR modulename)
EXTRN __imp_GetModuleHandleA:PROC
; FARPROC GetProcAddress (HMODULE modulehandle, LPCSTR procname)
EXTRN __imp_GetProcAddress:PROC
The next step is to prepare some static global variables in the .data
section.
; Static data
_DATA SEGMENT
User32dll db "User32.dll", 0h
Kernel32dll db "kernel32.dll", 0h
Beep db "Beep", 0h
LoadLibraryA db "LoadLibraryA", 0h
MessageBoxA db "MessageBoxA", 0h
ExitProcess db "ExitProcess", 0h
Caption db "Hello!", 0h
Message db "Click on ", '"', "Ok", '"', '.', 0h
_DATA ENDS
We’ll also need global variables for the resolved APIs (function pointers), zero initialized.
; Uninitialized data
_BSS SEGMENT align(8) READ WRITE
User32Ptr dq 0000000000000000h
Kernel32Ptr dq 0000000000000000h
BeepPtr dq 0000000000000000h
MessageBoxAPtr dq 0000000000000000h
ExitProcessPtr dq 0000000000000000h
LoadLibraryAPtr dq 0000000000000000h
_BSS ENDS
Our code will live in the .text
section and contains 3 functions: start, setup and main.
start is the entry point and is responsible for the loader’s general code flow: it calls setup, then main and terminates the process.
; Code
_TEXT SEGMENT
; Entry point
start PROC
sub rsp, 8
call setup
call main
mov rcx, rax
call ExitProcessPtr
add rsp, 8
ret
start ENDP
setup is responsible for resolving APIs and saving the resulting function pointers in the .bss
section using GetModuleHandleA, LoadLibraryA and GetProcAddress.
; Resolves Win32 APIs and saves function ptrs as
; global variables for later use
setup PROC
sub rsp, 28h
lea rcx, Kernel32dll
call QWORD PTR __imp_GetModuleHandleA ; Kernel32Ptr = GetModuleHandleA ("kernel32.dll")
mov Kernel32Ptr, rax
mov rcx, Kernel32Ptr
lea rdx, LoadLibraryA
call QWORD PTR __imp_GetProcAddress ; LoadLibraryAPtr = GetProcAddress (Kernel32Ptr, "LoadLibraryA")
mov LoadLibraryAPtr, rax
lea rcx, User32dll
call LoadLibraryAPtr ; User32Ptr = LoadLibraryA ("user32.dll")
mov User32Ptr, rax
mov rcx, Kernel32Ptr
lea rdx, Beep
call QWORD PTR __imp_GetProcAddress ; BeepPtr = GetProcAddress (Kernel32Ptr, "Beep")
mov BeepPtr, rax
mov rcx, User32Ptr
lea rdx, MessageBoxA
call QWORD PTR __imp_GetProcAddress ; MessageBoxAPtr = GetProcAddress (User32Ptr, "MessageBoxA")
mov MessageBoxAPtr, rax
mov rcx, Kernel32Ptr
lea rdx, ExitProcess
call QWORD PTR __imp_GetProcAddress ; ExitProcessPtr = GetProcAddress (Kernel32Ptr, "ExitProcess")
mov ExitProcessPtr, rax
add rsp, 28h
ret
setup ENDP
Finally, we call main which is where the program starts using the collected artifacts:
; Just like nothing happened!
main PROC
sub rsp, 28h
xor ecx, ecx
lea rdx, Message
lea r8, Caption
xor r9d, r9d
call MessageBoxAPtr ; MessageBoxA (NULL, Message, Caption, NULL)
mov ecx, 100h
mov edx, 100h
call BeepPtr ; Beep (256, 256)
add rsp, 28h
ret
main ENDP
_TEXT ENDS
END
Assemble and Link:
Paste the following code in a file called make.bat
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@echo off
if ["%~1"]==[""] (
ml64 main.asm /link /entry:start /subsystem:windows /OUT:poc.exe kernel32.lib /nologo
) else if ["%~1"]==["clean"] (
del *.obj,*.exe,*.lnk
) else (
echo usage:
echo make - assembles and links main.asm into poc.exe.
echo make clean - deletes all .exe, .obj and .lnk files in the dir.
)
@echo on
This will serve as a rudimentary build system, and that’s all we really need in this case.
Issue the make
command.
If your copy pasta game is on point you should see our poc.exe
binary.
And that’s it!
We now have a functional piece of code that we can use as reference.