Windows API in assembly using MinGW assembler – 1

There are many good assemblers(masm, fasm, nasm etc) which come with almost everything one needs to create a windows application in assembly language. Most of them can be run on multiple operating systems. However, if someone is used to writing assembly code on Linux using gas assembler, it takes a while to get used to features and syntax differences of other assemblers. Fortunately, MinGW created a windows port of binutils, which comes with all the tools that one uses while creating assembly programs on Linux. But, as MinGW is essentially geared towards writing applications in C, we can’t use the binutils tools straight away without loosing productivity.

I have created include files for constants and couple of time-saving macros. Together with binutils, these include files and macros help writing windows applications in assembly language using MinGW as easy as that of writing in C, if not easier. I have put together some examples which highlight the usage of MinGW assembler features that are bare minimum to develop any useful windows application. If you are new to assembly and/or windows programming, this will be of no use as I did not explain things in detail.

Before showing an example of writing windows application in assembly using the bells and vigils, let’s see how it can be written with just MinGW installed.

# file - msgbox.asm
# without using include files and macros

.intel_syntax noprefix

.section .data
    window_title:
        .ascii "Message box\0"
    window_message:
        .ascii "It works!!!\0"

.section .text
    .global _start
    _start:

        # displays a message box
        push    0
        push    offset window_title
        push    offset window_message
        push    0
        call  _MessageBoxA@16
        mov     eax, 0

        # exit
        push    0
        call  _ExitProcess@4
.end

As shown above, there are two irritating things here. One, the value of constants that have to be pushed and the prefix and suffix to the name of the function. The above code seriously lacks readability and difficult to manage as it becomes lengthy. Now, let’s see the same code with some make-up. The following example uses include files which can be downloaded from here. Place the include files in D:\examples\includes and the source files in D:\examples

# file - msgbox.asm
# with include files and macros

.intel_syntax noprefix

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

.section .data
    window_title:
        .ascii "Message box\0"
    window_message:
        .ascii "It works!!!\0"

.section .text
    .global _start
    _start:

        # displays a message box
        invoke      MessageBox, NULL, "offset window_message", "offset window_title", MB_OK
        mov         eax, 0

        # exit
        invoke      ExitProcess, 0
.end

So, with few .include directives, this version looks more like a code which can be understood by anyone. macroA.inc and macroW.inc files have macros defined which let us use the function name in the code as it appears in the API and pass the arguments in the same order. All the other .inc files has the constants which that let us use constants like MB_OK in the code. Assembler replaces constants with the actual values and expand macros. It is important to note that, once the assembler replaces constants and expands macros, the code looks exactly the same as shows in the first version. So, there is no overhead of using these.

The above examples can be assembled and linked using the following commands. It is assumed that, all the .inc files are in D:\examples\includes and MinGW is installed in D:\MinGW

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

output:

The following example uses macroW.inc instead of macroA.inc used in the above example. macroW.inc uses unicode version of the API functions. It will be clear once you see the output of this example.

# file - msg_box.asm
.intel_syntax noprefix

.include "user32.inc"
.include "kernel32.inc"
# gives unicode character support
.include "macroW.inc"         

.section .data
    window_title:
        .word   3118, 3142, 3126, 3142, 3100, 3149, 0
    window_message:
        .word  3129, 3142, 3122, 3147,0      

.section .text
    .global _start
    _start:

        # displays a message box with title and message in telugu
        invoke      MessageBox, NULL, "offset window_message", "offset window_title", MB_OK
        mov         eax, 0

        # exit
        invoke      ExitProcess, 0

.end

output:

In the next installment, I will show an example of creating a dll (run-time and load-time).

2 responses to “Windows API in assembly using MinGW assembler – 1”

  1. Paul

    Hi,

    This is great.

    I have a question. How did you create those .inc files for mingw?

    Thanks
    Paul

Leave a Reply