对于 64 位可执行文件,manually-generated PE headers 中的映像库出现错误
Image base comes out wrong in manually-generated PE headers for a 64-bit executable
我想使用 NASM 构建自定义可移植可执行文件。使用命令 nasm -f bin program.asm -o program.exe
获取可执行文件后,它崩溃并给出类似于此
的错误
下面你可以看到代码。
bits 64
%define BASE 400000h
ALIGNMENT equ 512
%define SECTALIGN 8192
STD_OUTPUT_HANDLE equ -11
NULL equ 0
%define ROUND(v, a) (((v + a - 1) / a) * a)
%define ALIGNED(v) (ROUND(v, ALIGNMENT))
%define RVA(obj) (obj - BASE)
section header progbits start=0 vstart=BASE
mz_hdr:
dw "MZ" ; DOS magic
times 0x3a db 0 ; [UNUSED] DOS header
dd RVA(pe_hdr) ; address of PE header
pe_hdr:
dw "PE",0 ; PE magic + 2 padding bytes
dw 0x8664 ; i386 architecture
dw 2 ; two sections
dd 0 ; [UNUSED] timestamp
dd 0 ; [UNUSED] symbol table pointer
dd 0 ; [UNUSED] symbol count
dw OPT_HDR_SIZE ; optional header size
dw 0x0002 ; characteristics: 32-bit, executable
opt_hdr:
dw 0x020b ; optional header magic
db 13,37 ; [UNUSED] linker version
dd ALIGNED(S_TEXT_SIZE) ; [UNUSED] code size
dd ALIGNED(S_IDATA_SIZE) ; [UNUSED] size of initialized data
dd 0 ; [UNUSED] size of uninitialized data
dd RVA(section..text.vstart) ; entry point address
dd RVA(section..text.vstart) ; [UNUSED] base of code
dd RVA(section..idata.vstart) ; [UNUSED] base of data
dd BASE ; image base
dd SECTALIGN ; section alignment
dd ALIGNMENT ; file alignment
dw 4,0 ; [UNUSED] OS version
dw 0,0 ; [UNUSED] image version
dw 4,0 ; subsystem version
dd 0 ; [UNUSED] Win32 version
dd RVA(the_end) ; size of image
dd ALIGNED(ALL_HDR_SIZE) ; size of headers
dd 0 ; [UNUSED] checksum
dw 3 ; subsystem = console
dw 0 ; [UNUSED] DLL characteristics
dd 0x00100000 ; [UNUSED] maximum stack size
dd 0x00001000 ; initial stack size
dd 0x00100000 ; maximum heap size
dd 0x00001000 ; [UNUSED] initial heap size
dd 0 ; [UNUSED] loader flags
dd 16 ; number of data directory entries
dd 0,0 ; no export table
dd RVA(import_table) ; import table address
dd IMPORT_TABLE_SIZE ; import table size
times 14 dd 0,0 ; no other entries in the data directories
OPT_HDR_SIZE equ $ - opt_hdr
sect_hdr_text:
db ".text",0,0,0 ; section name
dd ALIGNED(S_TEXT_SIZE) ; virtual size
dd RVA(section..text.vstart) ; virtual address
dd ALIGNED(S_TEXT_SIZE) ; file size
dd section..text.start ; file position
dd 0,0 ; no relocations or debug info
dw 0,0 ; no relocations or debug info
dd 0x60000020 ; flags: code, readable, executable
sect_hdr_idata:
db ".idata",0,0 ; section name
dd ALIGNED(S_IDATA_SIZE) ; virtual size
dd RVA(section..idata.vstart) ; virtual address
dd ALIGNED(S_IDATA_SIZE) ; file size
dd section..idata.start ; file position
dd 0,0 ; no relocations or debug info
dw 0,0 ; no relocations or debug info
dd 0xC0000040 ; flags: data, readable, writeable
ALL_HDR_SIZE equ $ - $$
;;;;;;;;;;;;;;;;;;;; .text ;;;;;;;;;;;;;;;;;
section .text progbits follows=header align=ALIGNMENT vstart=BASE+SECTALIGN*1
s_text:
; set up stack frame for *lpBytesWritten
; push ebp
; sub esp, 4
; push STD_OUTPUT_HANDLE
; call [GetStdHandle]
; push NULL
; push buffer
; push message_size
; push message
; push eax
; call [WriteConsoleA]
push rbp
mov rbp, rsp
sub rsp, 40
push 0
call [ExitProcess]
mov rsp, rbp
pop rbp
S_TEXT_SIZE equ $ - s_text
;;;;;;;;;;;;;;;;;;;; .idata ;;;;;;;;;;;;;;;;;
section .idata progbits follows=.text align=ALIGNMENT vstart=BASE+SECTALIGN*2
s_idata:
; message db "Hello World!",0
; message_size equ $ - message
; buffer resd 2
; buffer2 resb 32
import_table:
; import of kernel32.dll
dd 0 ; [UNUSED] read-only IAT
dd 0 ; [UNUSED] timestamp
dd 0 ; [UNUSED] forwarder chain
dd RVA(N_kernel32) ; library name
dd RVA(IAT_kernel32) ; IAT pointer
; import of user32.dll
dd 0 ; [UNUSED] read-only IAT
dd 0 ; [UNUSED] timestamp
dd 0 ; [UNUSED] forwarder chain
dd RVA(N_user32) ; library name
dd RVA(IAT_user32) ; IAT pointer
; terminator (empty item)
times 5 dd 0
IMPORT_TABLE_SIZE: equ $ - import_table
IAT_kernel32:
ExitProcess: dd RVA(H_ExitProcess)
GetStdHandle: dd RVA(H_GetStdHandle)
WriteConsoleA: dd RVA(H_WriteConsoleA)
dd 0
IAT_user32:
MessageBoxA: dd RVA(H_MessageBoxA)
dd 0
align 4, db 0
N_kernel32: db "kernel32.dll",0
align 4, db 0
N_user32: db "user32.dll",0
align 2, db 0
H_MessageBoxA: db 0,0,"MessageBoxA",0
align 2, db 0
H_GetStdHandle: db 0,0,"GetStdHandle",0
align 2, db 0
H_WriteConsoleA: db 0,0,"WriteConsoleA",0
align 2, db 0
H_ExitProcess: db 0,0,"ExitProcess",0
S_IDATA_SIZE equ $ - s_idata
align ALIGNMENT, db 0
the_end:
结果很奇怪,因为如果我在 header 中使用 dd
作为图像基本变量,我得到的结果将左移 8 位并添加一些不相关的位 (1)。所有其他变量似乎都没问题。
但是如果我使用 dq
作为图像基础,它的值保持不变 (2) 但其他相关值向右移动。 (3)
这是从 32 位汇编示例移植而来的。我需要添加或更改一些 header 值吗?
由于您使用的是 PE32+ 格式(由 0x020b 幻数标识),因此您应该 而不是 在您的可选 header 中有一个 BaseOfData
字段].
此外,ImageBase
应该是四字,Stack/Heap
字段也应该是。
参见 the documentation。
我想使用 NASM 构建自定义可移植可执行文件。使用命令 nasm -f bin program.asm -o program.exe
获取可执行文件后,它崩溃并给出类似于此
下面你可以看到代码。
bits 64
%define BASE 400000h
ALIGNMENT equ 512
%define SECTALIGN 8192
STD_OUTPUT_HANDLE equ -11
NULL equ 0
%define ROUND(v, a) (((v + a - 1) / a) * a)
%define ALIGNED(v) (ROUND(v, ALIGNMENT))
%define RVA(obj) (obj - BASE)
section header progbits start=0 vstart=BASE
mz_hdr:
dw "MZ" ; DOS magic
times 0x3a db 0 ; [UNUSED] DOS header
dd RVA(pe_hdr) ; address of PE header
pe_hdr:
dw "PE",0 ; PE magic + 2 padding bytes
dw 0x8664 ; i386 architecture
dw 2 ; two sections
dd 0 ; [UNUSED] timestamp
dd 0 ; [UNUSED] symbol table pointer
dd 0 ; [UNUSED] symbol count
dw OPT_HDR_SIZE ; optional header size
dw 0x0002 ; characteristics: 32-bit, executable
opt_hdr:
dw 0x020b ; optional header magic
db 13,37 ; [UNUSED] linker version
dd ALIGNED(S_TEXT_SIZE) ; [UNUSED] code size
dd ALIGNED(S_IDATA_SIZE) ; [UNUSED] size of initialized data
dd 0 ; [UNUSED] size of uninitialized data
dd RVA(section..text.vstart) ; entry point address
dd RVA(section..text.vstart) ; [UNUSED] base of code
dd RVA(section..idata.vstart) ; [UNUSED] base of data
dd BASE ; image base
dd SECTALIGN ; section alignment
dd ALIGNMENT ; file alignment
dw 4,0 ; [UNUSED] OS version
dw 0,0 ; [UNUSED] image version
dw 4,0 ; subsystem version
dd 0 ; [UNUSED] Win32 version
dd RVA(the_end) ; size of image
dd ALIGNED(ALL_HDR_SIZE) ; size of headers
dd 0 ; [UNUSED] checksum
dw 3 ; subsystem = console
dw 0 ; [UNUSED] DLL characteristics
dd 0x00100000 ; [UNUSED] maximum stack size
dd 0x00001000 ; initial stack size
dd 0x00100000 ; maximum heap size
dd 0x00001000 ; [UNUSED] initial heap size
dd 0 ; [UNUSED] loader flags
dd 16 ; number of data directory entries
dd 0,0 ; no export table
dd RVA(import_table) ; import table address
dd IMPORT_TABLE_SIZE ; import table size
times 14 dd 0,0 ; no other entries in the data directories
OPT_HDR_SIZE equ $ - opt_hdr
sect_hdr_text:
db ".text",0,0,0 ; section name
dd ALIGNED(S_TEXT_SIZE) ; virtual size
dd RVA(section..text.vstart) ; virtual address
dd ALIGNED(S_TEXT_SIZE) ; file size
dd section..text.start ; file position
dd 0,0 ; no relocations or debug info
dw 0,0 ; no relocations or debug info
dd 0x60000020 ; flags: code, readable, executable
sect_hdr_idata:
db ".idata",0,0 ; section name
dd ALIGNED(S_IDATA_SIZE) ; virtual size
dd RVA(section..idata.vstart) ; virtual address
dd ALIGNED(S_IDATA_SIZE) ; file size
dd section..idata.start ; file position
dd 0,0 ; no relocations or debug info
dw 0,0 ; no relocations or debug info
dd 0xC0000040 ; flags: data, readable, writeable
ALL_HDR_SIZE equ $ - $$
;;;;;;;;;;;;;;;;;;;; .text ;;;;;;;;;;;;;;;;;
section .text progbits follows=header align=ALIGNMENT vstart=BASE+SECTALIGN*1
s_text:
; set up stack frame for *lpBytesWritten
; push ebp
; sub esp, 4
; push STD_OUTPUT_HANDLE
; call [GetStdHandle]
; push NULL
; push buffer
; push message_size
; push message
; push eax
; call [WriteConsoleA]
push rbp
mov rbp, rsp
sub rsp, 40
push 0
call [ExitProcess]
mov rsp, rbp
pop rbp
S_TEXT_SIZE equ $ - s_text
;;;;;;;;;;;;;;;;;;;; .idata ;;;;;;;;;;;;;;;;;
section .idata progbits follows=.text align=ALIGNMENT vstart=BASE+SECTALIGN*2
s_idata:
; message db "Hello World!",0
; message_size equ $ - message
; buffer resd 2
; buffer2 resb 32
import_table:
; import of kernel32.dll
dd 0 ; [UNUSED] read-only IAT
dd 0 ; [UNUSED] timestamp
dd 0 ; [UNUSED] forwarder chain
dd RVA(N_kernel32) ; library name
dd RVA(IAT_kernel32) ; IAT pointer
; import of user32.dll
dd 0 ; [UNUSED] read-only IAT
dd 0 ; [UNUSED] timestamp
dd 0 ; [UNUSED] forwarder chain
dd RVA(N_user32) ; library name
dd RVA(IAT_user32) ; IAT pointer
; terminator (empty item)
times 5 dd 0
IMPORT_TABLE_SIZE: equ $ - import_table
IAT_kernel32:
ExitProcess: dd RVA(H_ExitProcess)
GetStdHandle: dd RVA(H_GetStdHandle)
WriteConsoleA: dd RVA(H_WriteConsoleA)
dd 0
IAT_user32:
MessageBoxA: dd RVA(H_MessageBoxA)
dd 0
align 4, db 0
N_kernel32: db "kernel32.dll",0
align 4, db 0
N_user32: db "user32.dll",0
align 2, db 0
H_MessageBoxA: db 0,0,"MessageBoxA",0
align 2, db 0
H_GetStdHandle: db 0,0,"GetStdHandle",0
align 2, db 0
H_WriteConsoleA: db 0,0,"WriteConsoleA",0
align 2, db 0
H_ExitProcess: db 0,0,"ExitProcess",0
S_IDATA_SIZE equ $ - s_idata
align ALIGNMENT, db 0
the_end:
结果很奇怪,因为如果我在 header 中使用 dd
作为图像基本变量,我得到的结果将左移 8 位并添加一些不相关的位 (1)。所有其他变量似乎都没问题。
但是如果我使用 dq
作为图像基础,它的值保持不变 (2) 但其他相关值向右移动。 (3)
这是从 32 位汇编示例移植而来的。我需要添加或更改一些 header 值吗?
由于您使用的是 PE32+ 格式(由 0x020b 幻数标识),因此您应该 而不是 在您的可选 header 中有一个 BaseOfData
字段].
此外,ImageBase
应该是四字,Stack/Heap
字段也应该是。
参见 the documentation。