Нарушение прав доступа при вызове WinAPI InternetOpenA() из сборки

Я пытаюсь установить HTTP-соединение из сборки с помощью Windows API. Код следующий:

%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

Соответствующий Makefile:

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

Когда я запускаю его, возвращается код ошибки -1073741819 или 0xC0000005, как указал @selbie, или также известный как нарушение прав доступа. Я получил segfault как для wininet, так и для winhttp при создании дескриптора. Я прилагаю короткий код C, показывающий, что именно я хотел бы сделать: (Сборка не должна иметь отпечатков. Она находится только в исходном коде C для целей отладки)

#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;
}

Для ясности компилирую для 64-битной Windows. Я также статически привязываюсь, как показано на Makefile. Ваше здоровье

Обновлено: Код для некоторой отладки:

[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

57
1

Ответ:

Решено

После нескольких часов попыток и неудач мой коллега-программист @sbdswr из Discord помог мне отладить его — как ни странно, без всякого отладчика. Проблема действительно заключалась в моей обработке указателя стека, как уже указывал @Peter Cordes. Вот полная версия исправленного кода. Спасибо за помощь!

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