Search code examples
windowsassemblyx86nasmcalling-convention

How can I get a string from the global atom table?


I've been trying to program in assembly for the windows OS for a few days now. I'm trying to store a string in the global atom table and getting it back afterwards.

The problem is: When I call the function GlobalGetAtomNameA, the program crashes with code 5, access violation! Here's the code I wrote:

GLOBAL  _main   ; Entrypoint

EXTERN  printf                      ; msvcrt.dll

EXTERN  GlobalAddAtomA
EXTERN  GlobalDeleteAtom
EXTERN  GlobalFindAtomA
EXTERN  GlobalGetAtomNameA          ; kernel32.dll (and umengx86.dll?)

EXTERN  SetLastError
EXTERN  GetLastError                ; Errhandlingapi.h

EXTERN  LoadLibraryA                ; libloaderapi.h

EXTERN  ExitProcess

SEGMENT .data
LoadLib     DB "User32", 0x00

AtomString  DB "This is a string I want to store in the Global Atom Table!", 0x00
AtomFailed  DB "Oops! Something went wrong:", 0x0A, 0x00
AtomSucceed DB "Data stored successfully!", 0x00

String1     DB "Here is the string:", 0x0A, 0x00

ErrCode5    DB "Error: Access denied!", 0x00

SEGMENT .bss
AtomPTR:        resb 2          ; Atom will be 2 bytes long
AtomBuffer:     resb 100        ; Where the received message gets stored

SEGMENT .text
_main:
    PUSH LoadLib
    CALL LoadLibraryA           ; explicitly load this library into memory
    ADD esp, 4                  ; if this isn't done, GlobalAddAtomA will return NULL

    CMP eax, 0x00               ; if function returns 0, something went wrong
    JE Fail



    PUSH AtomString
    CALL GlobalAddAtomA         ; Call function
    ADD esp, 4                  ; Data stored as atom can have a maximum size of 255 bytes

    CMP ax, 0x00
    JE Fail



    MOV WORD[AtomPTR], ax           ; Copy Atom into memory (16 bits)

    PUSH AtomSucceed
    CALL printf
    ADD esp, 4



    PUSH AtomString
    CALL GlobalFindAtomA            ; Search for our string
    ADD esp, 4

    MOV bx, WORD[AtomPTR]           
    CMP ax, bx                      ; CMP these two atoms
    JNE Fail                        ; If they don't match, something went wrong



    PUSH 0x64                       ; 3rd arg: Buffer size
    PUSH AtomBuffer                 ; 2nd arg: Buffer for the retrieved value
    PUSH ax                         ; 1st arg: Atom

    CALL GlobalGetAtomNameA         ; Get String back
   ; Program crashes inside this function

    ADD esp, 10                     ; 3rd & 2nd arg: 4 bytes | ax: 2 bytes



    PUSH AtomBuffer
    CALL printf
    ADD esp, 4                      ; Print out the retrieved string



    XOR eax, eax
    PUSH eax
    CALL SetLastError               ; Set last error to 0x00
    ADD esp, 4

    MOV ax, WORD[AtomPTR]
    PUSH ax
    CALL GlobalDeleteAtom           ; Delete Atom from Table
    ADD esp, 2                      ; ax is only 2 bytes

    CALL GetLastError               ; If last error has changed from 0x00,
    CMP ax, 0x00                    ; something went wrong
    JNE Fail


ExitProc:
    XOR eax, eax
    PUSH eax
    CALL ExitProcess

; ----------------------------------------------------- ;   

Fail:
    PUSH AtomFailed
    CALL printf
    ADD esp, 4



    CALL GetLastError       ; No arguments

    CMP ax, 0x05
    JNE ExitProc            ; If not error code 5, exit

    PUSH ErrCode5           ; If error code 5, printf
    CALL printf
    ADD esp, 4



    JMP ExitProc            ; Exit

I use nasm to make an .obj file:

nasm -fwin32 Atoms.asm

And I link it with GoLink:

GoLink.exe /mix /console /entry _main Atoms.obj kernel32.dll user32.dll msvcrt.dll libloaderapi.h Errhandlingapi.h

When I ran the program in the debugger, I saw that the program crashed in the address space of umengx86.dll. What is this file?

I tried to link umengx86.dll with my .obj file, but that didn't work either.

Can you tell me what the problem is?

Thanks for your help!


Solution

  • PUSH ax only pushes 16 bits but the calling convention passes narrow integers in a full 4-byte stack slot.

    Use movzx eax, word [...] loads of narrow data so you don't have to mess around with 16-bit operand-size.

    Then you can use push eax to push without garbage in the upper 2 bytes, in case your callee wants an int instead of a short. If it only looks at the low 2 bytes, it doesn't matter what was in the top half, but you definitely need push eax.