Windows API in assembly using MinGW assembler – 2

In this part, we will look at how to create load-time and run-time dlls using MinGW assembler. Writing code for dll is almost same writing any assembly code except for the following

- Define the exported functions with the suffix ‘_’
- List the exported functions section ‘.drectve’ without ‘_’

# file - dll.asm
# This file becomes a dll after assembling and linking and can be
# loaded during load-time or run-time

.intel_syntax noprefix
.include "macroA.inc"
.include "user32.inc"
.include "kernel32.inc"

.section .data
    msg_box_title:
        .ascii "Message Box Title\0"
    msg:
        .ascii "Hello DLL\0"

.section .text
	.global dllMain

	dllMain:
        # function prologue
        push	ebp
        mov	ebp, esp

        mov     ebx, dword ptr [ebp+12]

        cmp     ebx, DLL_PROCESS_DETACH
        jz      process_detach

        cmp     ebx, DLL_PROCESS_ATTACH
        jz      process_attach

        cmp     ebx, DLL_THREAD_ATTACH
        jz      thread_attach

        cmp     ebx, DLL_THREAD_DETACH
        jz      thread_detach

    process_attach:
        jmp     exit_dll

    thread_attach:
        jmp     exit_dll

    thread_detach:
        jmp     exit_dll

    process_detach:
        jmp     exit_dll

        # return boolean value
        mov     eax, TRUE

    # function epilogue
    exit_dll:
        mov     esp, ebp
        pop     ebp
        ret

    .global _hello
    _hello:
        # function prologue
        push	ebp
        mov	ebp, esp

        invoke  MessageBox, 0, "offset msg", "offset msg_box_title", 0

        # function epilogue
        mov	  esp, ebp
        pop     ebp
        ret

.section .drectve
	.ascii " -export:hello"

Load-time DLL:
dll will be loaded the moment you load an executable into the memory. As you can see, when calling a function using load-time dll, you have to use the function name as defined in the dll, NOT as exported

# file - hello.asm
# calls a function in dll by loading the dll during load-time

.intel_syntax noprefix
.include "macroA.inc"
.include "kernel32.inc"

.section .data
    dll_lib:
        .ascii "message.dll\0"
    dll_func:
        .ascii "hello\0"

.section .text
	.globl _start
	_start:

	# calls hello function in the dll
        call	_hello

        invoke	ExitProcess, 0
.end

An example should clarify this.

Save the above two files in D:\examples and run the following commands. Make sure you also download include files from this website and place in D:\example\includes

as -o dll.o dll.asm -ID:\examples\includes
ld --out-implib libmessage.a -shared -s -o message.dll dll.o -LD:\MinGW\lib -luser32 -lkernel32
as -o hello.o hello.asm -ID:\examples\includes
ld --subsystem windows -o hello.exe hello.o -s -LD:\MinGW\lib -luser32 -lkernel32 -LD:\examples -lmessage

Run-time DLL:
dll is being loaded into memory programmatically, not when an executable is loaded into memory. To find the address of the function, pass the name of the function as exported in dll, NOT as defined.

# file - hello.asm
# calls a function in dll by loading the dll at run-time

.intel_syntax

.include "macroA.inc"
.include "kernel32.inc"

.section .data
    dll_lib:
        .ascii "message.dll\0"
    dll_func:
        .ascii "hello\0"

.section .text
    .globl _start
	_start:
        # load message.dll library
        invoke  LoadLibraryA, "offset dll_lib"

        # get the address of the hello exported function in message.dll
        invoke  GetProcAddress, eax, "offset dll_func"

        # call hello function
        invoke  eax

        invoke  ExitProcess, 0
.end

Run the following commands to see run-time loading in action.

as -o dll.o dll.asm -ID:\examples\includes
ld -s -shared -o message.dll dll.o -LD:\MinGW\lib -luser32 -lkernel32
as -o hello.o hello.asm -ID:\examples\includes
ld -s --subsystem windows -o hello.exe hello.o -LD:\MinGW\lib -luser32 -lkernel32

In the next part, we will see an example of shared memory in a dll.

Leave a Reply