Assembly 为部件(NASM)中的PE文件创建和使用节
我正在尝试完成这个仅用汇编生成的PE文件,它应该在控制台中显示一条消息。我希望以这样的方式组织它,以便以后可以轻松添加更多内容(知道在何处添加代码、数据、导入的函数)。Assembly 为部件(NASM)中的PE文件创建和使用节,assembly,crash,nasm,portable-executable,sections,Assembly,Crash,Nasm,Portable Executable,Sections,我正在尝试完成这个仅用汇编生成的PE文件,它应该在控制台中显示一条消息。我希望以这样的方式组织它,以便以后可以轻松添加更多内容(知道在何处添加代码、数据、导入的函数)。 我现在为代码、数据、未初始化数据和导入元素创建了4个部分。我现阶段的主要问题是: 节头中的某些值使可执行文件无效(没有有效的win32) 数据段中指向元素的指针错误 某些涉及首选绝对地址、节对齐和文件对齐的计算可能是错误的 首先,我将在下面显示我的所有代码。为了节省时间和便于阅读,将不添加一些真正无关紧要的内容 这是NASM代码
我现在为代码、数据、未初始化数据和导入元素创建了4个部分。我现阶段的主要问题是:
; 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
(bellowret
)中,它就会工作,但只要我将每个内容复制到它的适当部分,exe就会中断。看来地址算错了。从这里开始,节标题或其他地方可能有错误的值。你觉得怎么样 更新:
在实施了下面的更改之后,我现在遇到了一个问题。只要
.data
部分小于512字节,一切正常。一旦超过该值,我就会出现“奇怪的无效win32应用程序”错误
这里有两个由PEInfo导出的HTML文件。第一个包含工作文件的信息(其中.data
部分小于512字节):
当
.data
部分包含超过512字节时,第二个包含损坏的EXE的信息:
也许有人能发现崩溃的区别和原因。我现在有机会详细查看代码,并实际运行它。下面是我发现的所有问题 首先,你的尺寸计算似乎都不起作用。你可以这样做:
CODE_SIZE equ $$ - CODE
%define Round(Number, Multiple) Multiple+(Number/Multiple)
align 512 ; Align to 512 bytes in memory
db ".code", 0, 0, 0
dd Round(CODE_SIZE, $SECTION_ALIGNMENT) ; Size in memory
dd RVA(CODE) ; Start address in memory
dd Round(CODE_SIZE, $FILE_ALIGNMENT) ; Size on disk
dd CODE ; Start address on disk
db ".bss", 0, 0, 0, 0
dd Round(BSS_SIZE, $SECTION_ALIGNMENT) ; Size in memory
dd RVA(IMAGE_END) ; Start address in memory
dd 0 ; Size on disk
dd 0 ; Start address on disk
但是您尝试在定义它的行之前引用code\u SIZE
,因此它的计算结果为零
我的解决方案是添加端点标签,例如,code\u end:
,无论您通常在哪里执行这些计算。然后在代码的最开始,在使用这些值之前,计算每个块的结束标签和开始标签之间的差异大小
HEADERS_SIZE equ HEADERS_END - DOS_HEADER
CODE_SIZE equ CODE_END - CODE
DATA_SIZE equ DATA_END - DATA
IDATA_SIZE equ IDATA_END - IDATA
I_TABLE_SIZE equ I_TABLE_END - I_TABLE
下一个大问题是您的Round
宏,它如下所示:
CODE_SIZE equ $$ - CODE
%define Round(Number, Multiple) Multiple+(Number/Multiple)
align 512 ; Align to 512 bytes in memory
db ".code", 0, 0, 0
dd Round(CODE_SIZE, $SECTION_ALIGNMENT) ; Size in memory
dd RVA(CODE) ; Start address in memory
dd Round(CODE_SIZE, $FILE_ALIGNMENT) ; Size on disk
dd CODE ; Start address on disk
db ".bss", 0, 0, 0, 0
dd Round(BSS_SIZE, $SECTION_ALIGNMENT) ; Size in memory
dd RVA(IMAGE_END) ; Start address in memory
dd 0 ; Size on disk
dd 0 ; Start address on disk
我不确定你认为你在那里做什么,但这更像是你需要的:
%define Round(Number, Multiple) (Number+Multiple-1)/Multiple*Multiple
您需要确保该数字是倍数的倍数,因此是除法-乘法序列。您还需要将Multiple-1
添加到原始数字,以强制其向上取整
下一个大问题是RVA计算,或者说缺乏RVA计算。文件结构中有很多地方需要将偏移量指定为相对虚拟地址(RVA),这是内存中的相对偏移量。当您只按原样获取标签的值时,这就是磁盘上的偏移量
对于截面偏移,基本上需要将该偏移除以文件对齐方式,然后再乘以截面对齐方式。此外,代码块将在一个截面对齐偏移处加载,因此所有内容都应相对于代码块进行计算,然后将一个截面对齐添加到结果中
%define RVA(BaseAddress) (BaseAddress - CODE)/$FILE_ALIGNMENT*$SECTION_ALIGNMENT+$SECTION_ALIGNMENT
现在,这适用于分区边界上的地址。对于其他内容,您需要计算它们相对于节基址的内部偏移量,然后将其添加到该节的RVA中
%define RVA(Address,BaseAddress) RVA(BaseAddress)+(Address-BaseAddress)
这些计算假设各个部分已经与$FILE\u ALIGNMENT
值对齐,但事实并非如此。代码部分前面有一个align
,如下所示:
CODE_SIZE equ $$ - CODE
%define Round(Number, Multiple) Multiple+(Number/Multiple)
align 512 ; Align to 512 bytes in memory
db ".code", 0, 0, 0
dd Round(CODE_SIZE, $SECTION_ALIGNMENT) ; Size in memory
dd RVA(CODE) ; Start address in memory
dd Round(CODE_SIZE, $FILE_ALIGNMENT) ; Size on disk
dd CODE ; Start address on disk
db ".bss", 0, 0, 0, 0
dd Round(BSS_SIZE, $SECTION_ALIGNMENT) ; Size in memory
dd RVA(IMAGE_END) ; Start address in memory
dd 0 ; Size on disk
dd 0 ; Start address on disk
但是你需要在每一节之前以及在文件末尾的每一节之前都这样做。我还建议使用$FILE\u对齐
常量,否则就没有意义了
align $FILE_ALIGNMENT ; Align to 512 bytes in memory
除此之外,您还需要去掉所有的部分
声明。例如,所有这些行都需要删除
section .header progbits vstart=0
section .scode vstart=$SECTION_ALIGNMENT align=16
section .sdata vstart=$SECTION_ALIGNMENT*2 progbits align=4
section .sbss vstart=$SECTION_ALIGNMENT*3 align=4
section .sidata vstart=$SECTION_ALIGNMENT*4 align=4
因为您是手动构建整个文件格式,所以它们没有任何用途,并且它们阻止您使用横截面边界的标签进行偏移计算(这是我们几乎到处都需要的)
现在我们已经将所有内容正确对齐,并且有了两个RVA宏,我们可以开始修复需要使用RVA的代码的各个部分
首先在可选的头中,我们有代码RVA、数据RVA和入口点。此外,在这里,我认为各种尺寸值应指定为截面线形的倍数
dd Round(CODE_SIZE, $SECTION_ALIGNMENT)
dd Round(DATA_SIZE, $SECTION_ALIGNMENT) ; Initialized data size
dd Round(BSS_SIZE, $SECTION_ALIGNMENT) ; Uninitiated data size
dd RVA(CODE) ; Entry point
dd RVA(CODE) ; Code RVA
dd RVA(DATA) ; Data RVA
dd Round(HEADERS_SIZE, $FILE_ALIGNMENT) ; Headers size
另外,在可选标题中,当我认为应该四舍五入到文件对齐方式时,您可以将标题大小四舍五入到节对齐方式
dd Round(CODE_SIZE, $SECTION_ALIGNMENT)
dd Round(DATA_SIZE, $SECTION_ALIGNMENT) ; Initialized data size
dd Round(BSS_SIZE, $SECTION_ALIGNMENT) ; Uninitiated data size
dd RVA(CODE) ; Entry point
dd RVA(CODE) ; Code RVA
dd RVA(DATA) ; Data RVA
dd Round(HEADERS_SIZE, $FILE_ALIGNMENT) ; Headers size
这是事实上没有任何区别的事情之一——代码将以任何方式工作——但我仍然认为这是错误的,应该予以纠正
类似地,正如我在第一个回答中指出的,即使没有使用所有16个条目,数据目录表的大小也应该始终设置为16。如果你不这样做,它看起来确实有效,但我再次建议你正确地这样做
dd 16 ; Data directory entries
dd 0 ; Export table pointer
dd 0 ; Export table size
dd RVA(I_TABLE,IDATA) ; Import table pointer
dd I_TABLE_SIZE ; Size of import table
times 14 dq 0 ; Space the other 14 entries
另外,请注意,I_表偏移已更新为使用相对于IDATA节的RVA
下一步在表中,