assemblywinapisegmentation-faultnasm

Access Violation when calling WinAPI's InternetOpenA() from assembly


I'm trying to establish an HTTP connection from assembly using the Windows API. The code is the following:

%include "const.asm"
%define u(x) __utf16__(x) 
%define w(x) __utf32__(x) 

; Externs
extern InternetOpenA

extern GetLastError

section .data
    curProc   db 0
    hInternet dq 0

    lpszAgent db "Mozilla/5.0", 0

section .text
global Start
Start:
    sub rsp, 8

    sub rsp, 32 + 1 * 8
    lea rcx, [REL lpszAgent]                            ; lpszAgentW (WIDE STRING)
    mov rdx, 4        ; dwAccessType (DWORD)
    mov r8,  NULL                                       ; lpszProxy (DWORD)
    mov r9,  NULL                                       ; pszProxyBypassW (DWORD)
    mov dword [rsp + 4 * 8], NULL                       ; dwFlags (DWORD)
    call InternetOpenA

    mov [REL hInternet], rax 
    add rsp, 32 + 1 * 8                                 ; hInternet parameter
    cmp rax, NULL
    je error_handler

    ; Stop
    jmp exit


error_handler:
    sub rsp, 32
    call GetLastError                                    ; Load more error info
    add rsp, 32 + 8
    ret


exit:                                                    ; Graceful shutdowns
    xor rax, rax
    add rsp, 8
    ret

The corresponding Makefile is:

INCLUDES := kernel32.lib user32.lib wininet.lib

.PHONY: all compile link clean

all: build

compile:
    @echo "Compiling assembly"
    nasm -f win64 main.asm -o main.o

link: compile
    @echo "Linking assembly"
    polink /ENTRY:Start /SUBSYSTEM:CONSOLE /LIBPATH:C:\lib64 $(INCLUDES) main.o

build: link

clean:
    @echo "Cleaning up"
    del main.o

When I'm running it, the error code returned is -1073741819 or 0xC0000005 as @selbie pointed out, or also known as Access Violation. I've received segfault for both wininet and winhttp while creating the handle. I'm attaching a short C code showing exactly what I would like to do: (The assembly should not have the prints. It is just in the C source for debug purposes)

#include "windows.h"
#include "wininet.h"
#include <stdio.h>

int main() {
    const char *agent = "Mozilla/5.0";
    HINTERNET handler =  InternetOpenA(agent, INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY, 0, 0, 0);
    if (handler) {
        return 0;
    }
    printf("%d", GetLastError());
    return 1;
}

Just for clarity, I am compiling for 64-bit Windows. I am also statically linking as seen in the Makefile. Cheers

Edit: Code for some debugging:

[BITS 64]

%include "const.asm"
%define u(x) __utf16__(x) 
%define w(x) __utf32__(x) 

; Externs
extern InternetOpenA

extern GetLastError

extern GetStdHandle
extern WriteFile

section .data
    curProc   db 0
    hInternet dq 0

    lpszAgent db "Mozilla/5.0", 0

    NtlpBuffer:        db    'Hello, World!', 0
    NtnNBytesToWrite:  dq    0eh


section .text
global Start
Start:

    sub rsp, 8
    mov rax, rsp    
    mov rbx, 8 
    xor rdx, rdx
    div rbx
    cmp rdx, 0
    je main

    sub rsp, 32 + 1 * 8
    and rsp, -16
    lea rcx, [REL lpszAgent]                            ; lpszAgentW (WIDE STRING)
    mov rdx, 4                                          ; dwAccessType (DWORD)
    mov r8,  NULL                                       ; lpszProxy (DWORD)
    mov r9,  NULL                                       ; pszProxyBypassW (DWORD)
    mov dword [rsp + 4 * 8], NULL                       ; dwFlags (DWORD)
    
    call InternetOpenA

    mov [REL hInternet], rax 
    add rsp, 32 + 1 * 8                                 ; hInternet parameter
    cmp rax, NULL
    je error_handler

    ; Stop
    jmp exit


error_handler:
    sub rsp, 32
    call GetLastError                                    ; Load more error info
    add rsp, 32 + 8
    ret


exit:                                                    ; Graceful shutdowns
    xor rax, rax
    add rsp, 8
    ret

main:
    sub     rsp, 32
    mov     ecx, -11
    call    GetStdHandle

    mov     rcx, rax
    lea rdx, [rel NtlpBuffer]
    mov     r8, [REL NtnNBytesToWrite]
    mov     r9, NULL
    mov     qword [rsp + 32], 00h
    call    WriteFile
    add     rsp, 32

ExitProgram:
    xor     eax, eax
    add rsp, 8
    ret

Solution

  • After hours of trying and failing, a fellow assembly programmer, @sbdswr, from Discord helped me debugging it -- funnily enough without any debugger. The problem indeed lay in my stack pointer handling as @Peter Cordes already pointed out. Here is the full version of the fixed code. Thank you for your help!

    BITS 64
    
    %include "const.asm"
    %define u(x) __utf16__(x) 
    %define w(x) __utf32__(x) 
    
    ; Externs
    extern InternetOpenA
    
    extern GetLastError
    
    extern GetStdHandle
    extern WriteFile
    
    extern ExitProcess
    
    section .data
        curProc   db 0
        hInternet dq 0
    
        lpszAgent db "Mozilla/5.0", 0
    
        NtlpBuffer:        db    'Hello, World!', 0
        NtnNBytesToWrite:  dq    0eh
    
    
    section .text
    global Start
    Start:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 48
        
        lea     rcx, [REL lpszAgent]                            ; lpszAgentW (WIDE STRING)
        mov     rdx, 4                                          ; dwAccessType (DWORD)
        mov     r8,  NULL                                       ; lpszProxy (DWORD)
        mov     r9,  NULL                                       ; pszProxyBypassW (DWORD)
        mov     dword [rsp + 4 * 8], NULL                       ; dwFlags (DWORD)
        call    InternetOpenA
        
        mov     [REL hInternet], rax                            ; hInternet parameter
        cmp     rax, NULL
        je      error_handler
    
        jmp     exit
    
    
    error_handler:
        call    GetLastError                                    ; Load more error info
        jmp     exit
    
    
    exit:
        call    print
        xor     rcx, rcx
        call    ExitProcess
        
    print:
        sub     rsp, 40
        mov     ecx, -11
        call    GetStdHandle
    
        mov     rcx, rax
        lea     rdx, [rel NtlpBuffer]
        mov     r8, [REL NtnNBytesToWrite]
        mov     r9, NULL
        mov     qword [rsp + 32], 00h
        call    WriteFile
        add     rsp, 40
        ret