从自定义 PE 文件中的 dll 导入时,无法找到过程入口点 MessageBoxA

procedure entry point MessageBoxA could not be located when importing from dll in a custom PE file

我一直在努力掌握 PE 文件格式(只是为了自学一些新东西)并尝试创建一个简单的 .exe 它显示一个消息框然后退出,我已经在格式上使用 tinyPE as a base and then built on top of it using stuff from the microsoft docs

问题是,当我尝试 运行 它时,我得到一个 The procedure entry point MessageBoxA could not be located in the dynamic link library D:\path\to\my\test.exe 弹出窗口(听起来 windows 试图从 exe 本身导入函数,但我不不明白为什么会那样做)

我尝试重新排列内容并尝试 headers 但没有任何帮助。我还使用 PE Explorer 检查了导入,但它似乎正确读取了导入并说它应该按预期从 kernel32.dll 导入

这是我的代码:

bits 32

align 1, db 0

mz_header:
    dw "MZ"                       ; e_magic
    dw 0                          ; e_cblp
    dw 0                          ; e_cp
    dw 0                          ; e_crlc
    dw 0                          ; e_cparhdr
    dw 0                          ; e_minalloc
    dw 0                          ; e_maxalloc
    dw 0                          ; e_ss
    dw 0                          ; e_sp
    dw 0                          ; e_csum
    dw 0                          ; e_ip
    dw 0                          ; e_cs
    dw 0                          ; e_lsarlc
    dw 0                          ; e_ovno
    times 4 dw 0                  ; e_res
    dw 0                          ; e_oemid
    dw 0                          ; e_oeminfo
    times 10 dw 0                 ; e_res2
    dd pe_header                  ; e_lfanew

pe_header:
    dd "PE"
    dw 0x014C                     ; Machine (Intel 386)
    dw 2                          ; NumberOfSections
    dd 0x4545BE5D                 ; TimeDateStamp
    dd 0                          ; PointerToSymbolTable
    dd 0                          ; NumberOfSymbols
    dw pe_optional_header_size    ; SizeOfOptionalHeader
    dw 0x103                      ; Characteristics (no relocations, executable, 32 bit)

pe_optional_header:
    dw 0x10B                      ; Magic (PE32)
    db 8                          ; MajorLinkerVersion
    db 0                          ; MinorLinkerVersion
    dd text_size                  ; SizeOfCode
    dd 0                          ; SizeOfInitializedData
    dd 0                          ; SizeOfUninitializedData
    dd _main                      ; AddressOfEntryPoint
    dd text_begin                 ; BaseOfCode
    dd filesize                   ; BaseOfData
    dd 0x400000                   ; ImageBase
    dd 1                          ; SectionAlignment
    dd 1                          ; FileAlignment
    dw 4                          ; MajorOperatingSystemVersion
    dw 0                          ; MinorOperatingSystemVersion
    dw 0                          ; MajorImageVersion
    dw 0                          ; MinorImageVersion
    dw 4                          ; MajorSubsystemVersion
    dw 0                          ; MinorSubsystemVersion
    dd 0                          ; Win32VersionValue
    dd filesize                   ; SizeOfImage
    dd header_total_size          ; SizeOfHeaders
    dd 0                          ; CheckSum
    dw 2                          ; Subsystem (Win32 GUI)
    dw 0x400                      ; DllCharacteristics
    dd 0x100000                   ; SizeOfStackReserve
    dd 0x1000                     ; SizeOfStackCommit
    dd 0x100000                   ; SizeOfHeapReserve
    dd 0x1000                     ; SizeOfHeapCommit
    dd 0                          ; LoaderFlags
    dd 4                          ; NumberOfRvaAndSizes

rva:
    dd 0
    dd 0
    dd import_dir_table
    dd import_dir_table_size
    times 12 dd 0, 0              ; This is necessary for a valid executable, probably as padding

pe_optional_header_size equ $ - pe_optional_header

; Section table
text_section:
    db ".text", 0, 0, 0           ; Section name
    dd text_size                  ; Size when loaded
    dd header_total_size          ; Adress when loaded
    dd text_size                  ; Size
    dd text_begin                 ; Points to section beginning
    dd 0, 0                       ; Pointer to relocations and line numbers
    dw 0, 0                       ; Count of relocations and line numbers
    dd 0x60000020                 ; Characteristics
rdata_section:
    db ".rdata", 0, 0             ; Section name
    dd rdata_size                 ; Size when loaded
    dd rdata_begin                ; Adress when loaded
    dd rdata_size                 ; Size
    dd rdata_begin                ; Points to section beginning
    dd 0, 0                       ; Pointer to relocations and line numbers
    dw 0, 0                       ; Count of relocations and line numbers
    dd 0x40000040                 ; Characteristics
    
header_total_size equ $ - $$

; .text section
text_begin:
; Entry function
_main:
    ; try to call the imported function
    push dword 0
    push dword mbox_message + 0x400000 
    push dword 0
    push dword 1
    call [import_adress_table + 0x400000]
    
    mov eax, 42
    ret

text_size equ $ - text_begin

; .rdata section
rdata_begin equ $ - $$

import_dir_table:
    dd import_lookup_table                    ; Import lookup table
    dd 0, 0                                   ; Timestamp and forwarder chain, unused
    dd kernel32                               ; Name of dll
    dd import_adress_table                    ; Import adress table

    ; Empty entry to signify end of import dir table
    dd 0, 0, 0, 0, 0

import_dir_table_size equ $ - import_dir_table

; Like the lookup table, but entries are replaced
; with real adresses of imported functions
import_adress_table:
    dd namehint_table
    dd 0

; Stores DWORD pointers to Name/Hint tables
import_lookup_table:
    dd namehint_table
    dd 0

; Stores a hint (?) and a function name to import
namehint_table:
    db 0, 0
    db "MessageBoxA", 0   ; Function name
    
; String used to import the kernel32.dll
kernel32:
    db "kernel32.dll", 0
    
; Other .rdata stuff

mbox_message:
    db "Hello, World!", 0

rdata_size equ $ - rdata_begin
    
filesize equ $ - $$

我正在使用 NASM 2.15.5 进行组装:nasm -f bin -o test.exe test.asm

那么我哪里出错了?提前谢谢你

原问题是RbMm在评论中解决的,我错误地导入了kernel32.dll而不是user32.dll(我尝试调用ExitProcess时遗留下来的)

导入正确的库解决了错误,但要使其正常工作并显示消息框,我必须导入 MessageBoxA 调用的所有函数,要找到这些函数,只需创建一个调用 MessageBoxA 的测试程序,然后查看导入MSVC 生成:

#include <winuser.h>
int main()
{
    return MessageBoxA(NULL, "Test", NULL, 0);
}