gpt4 book ai didi

assembly - 为程序集中的PE文件创建和使用节(NASM)

转载 作者:行者123 更新时间:2023-12-03 15:52:30 26 4
gpt4 key购买 nike

我正在尝试完成仅用汇编制作的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(bellow ret)中,则可以正常工作,但是只要我将每件事物复制到应有的适当部分,该exe就会中断。
    因此,似乎地址计算错误。从这里开始,section header 或其他地方可能有错误的值。你怎么看?

    更新:
    在实施以下更改之后,我现在遇到了一个问题。只要 .data节小于512字节,一切都可以正常工作。一旦超过,我得到一个“奇怪的无效的Win32应用程序”错误。

    因此,这里有2个由PEInfo导出的HTML文件。第一个包含工作文件的信息(其中 .data节小于512字节):
    Working EXE PEInfo
    .data节包含的字节数超过512个字节时,第二个文件包含损坏的EXE的信息: Corrupt EXE PEInfo

    也许有人可以发现崩溃的差异和原因。

    最佳答案

    现在,我有机会详细查看代码并实际运行它。所以这是我发现的所有问题。

    首先,您的尺寸计算似乎都不起作用。您可以执行以下操作:

    CODE_SIZE equ $$ - CODE

    但是您尝试在定义它的行之前引用该 CODE_SIZE,因此它的值为零。

    我的解决方案是添加结束标签,例如 CODE_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宏,如下所示:
    %define Round(Number, Multiple)  Multiple+(Number/Multiple)

    我不确定您认为自己在那做什么,但这更像您所需要的:
    %define Round(Number, Multiple) (Number+Multiple-1)/Multiple*Multiple

    您要确保Number是倍数的倍数,因此要除以乘数序列。您还需要在原始Number上添加 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_ALIGNMENT值对齐,但实际上并非如此。您在代码部分之前有一个 align,如下所示:
    align   512 ; Align to 512 bytes in memory

    但是,您需要在每个单独的节之前以及文件末尾的每个节之前执行此操作。我也建议使用 $FILE_ALIGNMENT常量,否则就没有意义了。
    align   $FILE_ALIGNMENT ; Align to 512 bytes in memory

    除此之外,您还需要删除所有的 section声明。例如,所有这些行都需要删除。
    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的代码的各个部分。

    首先在可选 header 中,我们具有代码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

    同样在可选 header 中,当我认为 header 大小应四舍五入到文件对齐方式时, header 大小会四舍五入到部分对齐方式。
    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_TABLE偏移量更新为使用相对于IDATA部分的RVA。

    在您的部分表格中,接下来,所有偏移量都是错误的。例如,代码段标题的开头应如下所示:
    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  ".data", 0, 0, 0
    dd Round(DATA_SIZE, $SECTION_ALIGNMENT) ; Size in memory
    dd RVA(DATA) ; Start address in memory
    dd Round(DATA_SIZE, $FILE_ALIGNMENT) ; Size on disk
    dd DATA ; Start address on disk

    和idata部分:
    db  ".idata", 0, 0
    dd Round(IDATA_SIZE, $SECTION_ALIGNMENT) ; Size in memory
    dd RVA(IDATA) ; Start address in memory
    dd Round(IDATA_SIZE, $FILE_ALIGNMENT) ; Size on disk
    dd IDATA ; Start address on disk

    尽管bss部分略有不同。 bss部分的全部要点是不占用磁盘空间,但确实占用了内存空间。这意味着您实际上不能为bss数据包括任何数据定义。所以这段代码必须去:
    BSS:
    dd 5

    但这意味着磁盘上的部分与内存中的部分不匹配。为了简化RVA计算,我建议的解决方法是将bss节作为文件中的最后内容。当它的大小从磁盘上的0扩展到内存中不会影响任何其他偏移量的任何内容时。

    因此,我将在文件的最后添加一个名为 IMAGE_END:的标签,然后像这样定义bss部分:
    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

    请注意,由于地址需要按升序排列,因此该部分必须位于sections表中的idata部分之后。

    您可能想知道 BSS_SIZE值从何而来,如果代码中不再有bss部分。恐怕您将不得不手动定义该值。您还必须手动为该部分中任何变量的偏移量定义常量。如前所述,您不能使用数据定义,因为我们不希望它占用磁盘上的任何空间。

    接下来我们进入导入表。您为此使用的布局有些奇怪,但这似乎不是问题,因此我将保持原样。您确实需要更新所有地址才能使用RVA。

    首先是IAT:
    F_Sleep:         dd RVA(I_Sleep,IDATA)
    F_WriteConsole: dd RVA(I_WriteConsole,IDATA)
    F_GetStdHandle: dd RVA(I_GetStdHandle,IDATA)

    然后是导入描述符:
    .originalfthk    dd 0
    .timedate dd 0
    .forwarder dd 0
    .name dd RVA(kernel32,IDATA)
    .firstthunk dd RVA(IDATA,IDATA)

    我还应该提到,您在此描述符之后立即设置了 I_TABLE_S变量,如果您还记得的话,我说过您应该将这些尺寸计算替换为结束标签。但是,在这种情况下,描述符表的大小也应该包括最后的零条目。因此,放置结束标签的正确位置不在此处,而是在 times 20 db 0填充之后。
    times 20 db 0    
    I_TABLE_END:

    这是我认为不会有太大变化的另一件事,但我仍然建议您修复。

    同样,当您从一个DLL导入时,这种布局很好,但是当您需要更多时,您将需要更多的描述符和更多的IAT部分。因此,我建议您在每次IAT之前添加标签,例如在这种情况下,类似于 kernel32_iat。然后,您将第一个thunk初始化为。
    .firstthunk      dd RVA(kernel32_iat,IDATA)

    最后,我要处理 $IMAGE_SIZE的计算。您所使用的计算假设每个部分的大小都是固定的。但是,考虑到文件末尾有 IMAGE_END标签和RVA宏,我们可以轻松地将确切的图像大小计算为 RVA(IMAGE_END)

    但是,这没有考虑到bss部分,一旦将其加载到内存中,bss部分就会使图像变大。因此,图像尺寸的正确定义应为:
    $IMAGE_SIZE equ RVA(IMAGE_END) + Round(BSS_SIZE,$SECTION_ALIGNMENT)

    请注意,应在文件开头附近定义它-在任何地方使用它之前,但要定义 RVA宏和 BSS_SIZE之后。

    关于assembly - 为程序集中的PE文件创建和使用节(NASM),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17456372/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com