【发布时间】:2013-07-03 19:17:51
【问题描述】:
我正在尝试完成这个仅用程序集制作的 PE 文件,它应该在控制台中显示一条消息。我想以这样的方式组织它,以便以后轻松添加更多内容(知道在哪里添加代码、数据、导入的函数)。
我现在创建了 4 个部分,分别用于代码、数据、未启动的数据和导入的元素。我现阶段的主要问题是:
- 节头中的某些值使可执行文件无效(没有有效的win32)
- 指向数据部分元素的指针错误
- 一些涉及首选绝对地址、节对齐和文件对齐的计算可能是错误的
首先,我将在下面显示我的所有代码。一些真正无关紧要的东西不会添加,以节省时间并使其更易于阅读 这是 NASM 代码
; Constants (use '$' as prefix)
$SECTION_ALIGNMENT equ 4096 ; Each section is aligned to 4096 in memory
$FILE_ALIGNMENT equ 512 ; Each section is aligned to 512 on disk
$PREFERRED_ADDRESS equ 4194304 ; Preffered address for EXE is 4 MB
$TOTAL_PE_SECTIONS equ 4 ; Code, Data, Bss and IData
; Image size = headers aligned to section alignment + sections size aligned
; to next multiple of section alignment, everything aligned, too
$IMAGE_SIZE equ $SECTION_ALIGNMENT + (HEADERS_SIZE/$SECTION_ALIGNMENT) + \
$TOTAL_PE_SECTIONS * $SECTION_ALIGNMENT
; Will help us align some of the values to the next specified multiple
%define Round(Number, Multiple) Multiple+(Number/Multiple)
section .header progbits vstart=0
; This is the MZ header
DOS_HEADER:
db "MZ" ; MZ signature
; ...
; Here we have all the members of the DOS header, in 4 paragraphs
; ...
db PE_HEADER ; The last one is pointing to the PE header
DOS_STUB:
; ...
; A small DOS program to display a simple message in DOS (64 bytes)
; ...
; This is the PE header
PE_HEADER:
db "PE", 0, 0 ; PE signature
dw 0x014C ; Platform Intel I386
dw $TOTAL_PE_SECTIONS
dd 1371668450 ; Creation timestamp
dd 0 ; No symbols table
dd 0 ; No symbols
dw SECTIONS_TABLE - OPT_HEADER ; Optional header size
dw 0x0002|0x0004|0x0008|0x0100|0x0200 ; Characteristics
; Optional header
OPT_HEADER:
dw 0x010B ; Signature
db 0 ; Linker version
db 0 ; Minor linker version
dd CODE_SIZE
dd DATA_SIZE ; Initialized data size
dd BSS_SIZE ; Uninitiated data size
dd CODE ; Entry point
dd CODE ; Code RVA
dd DATA ; Data RVA
dd $PREFERRED_ADDRESS ; Preferred address in memory
dd $SECTION_ALIGNMENT
dd $FILE_ALIGNMENT
dw 4 ; OS version
dw 0 ; Minor OS version
dw 0 ; Image version
dw 0 ; Minor image version
dw 3 ; Subsystem version
dw 10 ; Minor subsystem version
dd 0 ; WIN32 version
dd $IMAGE_SIZE ; Image size calculated above
dd Round(HEADERS_SIZE, $SECTION_ALIGNMENT) ; Headers size
dd 0 ; Checksum
dw 3 ; System interface CUI
dw 0 ; DLL characteristics
dd 4096 ; Reserved stack
dd 4096 ; Still not ??
dd 65536 ; sure about ??
dd 0 ; these ??
dd 0
dd 2 ; Data directory entries
dd 0 ; Export table pointer
dd 0 ; Export table size
dd I_TABLE ; Import table pointer
dd I_TABLE_S ; Size of import table
dq 0 ; Reserved
SECTIONS_TABLE:
CODE_SECTION_HEADER:
db ".code", 0, 0, 0
dd Round(CODE_SIZE, $SECTION_ALIGNMENT) ; Size in memory
dd CODE
dd Round(CODE_SIZE, $FILE_ALIGNMENT) ; Size on disk
dd Round(0, $FILE_ALIGNMENT) ; Real start address
dd 0
dd 0
dw 0
dw 0
dd 0x00000020|0x20000000|0x40000000|0x80000000
DATA_SECTION_HEADER:
db ".data", 0, 0, 0
dd Round(DATA_SIZE, $SECTION_ALIGNMENT) ; Size in memory
dd $SECTION_ALIGNMENT * 2
dd Round(DATA_SIZE, $FILE_ALIGNMENT) ; Size on disk
dd Round(0, $FILE_ALIGNMENT) ; Real start address
dd 0
dd 0
dw 0
dw 0
dd 0x00000040|0x40000000|0x80000000
BSS_SECTION_HEADER:
db ".bss", 0, 0, 0, 0
dd Round(BSS_SIZE, $SECTION_ALIGNMENT) ; Size in memory
dd $SECTION_ALIGNMENT * 3
dd 0
dd 0
dd 0
dd 0
dw 0
dw 0
dd 0x00000080|0x40000000|0x80000000
IDATA_SECTION_HEADER:
db ".idata", 0, 0
dd Round(IDATA_SIZE, $SECTION_ALIGNMENT) ; Size in memory
dd $SECTION_ALIGNMENT * 4
dd 0
dd Round(0, $FILE_ALIGNMENT) ; Real start address
dd 0
dd 0
dw 0
dw 0
dd 0x00000040|0x40000000|0x80000000
HEADERS_SIZE equ $$ - DOS_HEADER
align 512 ; Align to 512 bytes in memory
section .scode vstart=$SECTION_ALIGNMENT align=16
use32
CODE:
push -11
call dword [$PREFERRED_ADDRESS + F_GetStdHandle]
push 0
push 0x402000
push 6
push $PREFERRED_ADDRESS + hello
push eax
call dword [$PREFERRED_ADDRESS + F_WriteConsole]
push -1
call dword [$PREFERRED_ADDRESS + F_Sleep]
ret
CODE_SIZE equ $$ - CODE
section .sdata vstart=$SECTION_ALIGNMENT*2 progbits align=4
DATA:
hello: db 'Hello!'
DATA_SIZE equ $$ - DATA
section .sbss vstart=$SECTION_ALIGNMENT*3 align=4
BSS:
dd 5
BSS_SIZE equ $$ - BSS
section .sidata vstart=$SECTION_ALIGNMENT*4 align=4
IDATA:
F_Sleep: dd I_Sleep
F_WriteConsole: dd I_WriteConsole
F_GetStdHandle: dd I_GetStdHandle
dd 0
I_TABLE:
.originalfthk dd 0
.timedate dd 0
.forwarder dd 0
.name dd kernel32
.firstthunk dd IDATA
I_TABLE_S equ $$ - I_TABLE
times 20 db 0
kernel32: db 'kernel32.dll', 0
I_Sleep:
dw 0
db 'Sleep', 0
align 2
I_WriteConsole:
dw 0
db 'WriteConsoleA', 0
align 2
I_GetStdHandle:
dw 0
db 'GetStdHandle', 0
IDATA_SIZE equ $$ - IDATA
这里的主要问题是可执行文件崩溃,因为来自代码部分的指针是错误的。我说的是来自.sdata 的指向hello 消息的指针以及来自.sidata 部分的指向导入函数的指针。如果我将 hello 变量和 .sidata 的全部内容都复制到 .scode (下面的 ret)中,它可以工作,但是一旦我将每个东西复制到应该是其正确部分的内容中,exe 就会中断。
因此,看起来地址计算错误。从这里开始,节标题或其他地方可能会有错误的值。你怎么看?
更新:
在实施以下更改后,我现在遇到了一个问题。只要.data 部分小于 512 字节,一切正常。一旦超过了,我就会收到“奇怪的无效 win32 应用程序”错误。
所以,这里有 2 个由 PEInfo 导出的 HTML 文件。第一个包含工作文件的信息(.data 部分小于 512 字节):
Working EXE PEInfo
第二个包含损坏的EXE的信息,当.data部分包含超过512个字节时:Corrupt EXE PEInfo
也许有人能看出崩溃的区别和原因。
【问题讨论】:
-
如何将文件加载到 PE Explorer 或其他类似工具中?这应该会提示您标头中的哪些字段可能不正确。
-
好久没做windows了!我看到的一件事......在你的dos标题的末尾:
db PE_HEADER。当然这应该是dd,不是吗?我怀疑这是否是您的问题(?),我同意部分地址可能无法正确计算。我认为迈克尔有个好主意! (FWIW,使用链接器更容易!)
标签: assembly crash nasm portable-executable sections