Windows 10 无法识别 hand-made 可在 WINE 中运行的 PE 可执行文件
Windows 10 won't recognize hand-made PE executables that work in WINE
我已经(在汇编器中,没有链接器)为 x86-64 制作了一个 EXE,它在 Linux 下的 Wine 中运行得非常好。这是调用 MessageBoxA 和 ExitProcess 的基本 HelloWorld。
Windows10不会认,说'This program cannot be executed on your computer, talk to your vendor for a version that will suit your computer'.
我使用PE 格式阅读器(PE 工具和CFF 资源管理器)来分析我的PE EXE。 PE Optional header 中的所有数字与其他工作 EXE 中的数字相同(如 os 版本、子系统版本)。只有特定于我的文件和部分的那些不同。而且 Windows 不会将该文件识别为我的计算机上的可执行文件。
除了 WIndows 错误消息,我还能从哪里开始查看?是否有任何工具可以使用比 'Bad exe' 更具体的错误消息来检查 EXE 有效性? (这是 xdbg 报告的内容。
在 Wine 上,我能够做到
WINEDEBUG=+all wine my.exe
这给了我错误的提示,我能够修复它并让它工作。 Windows 中有这样的工具吗?
BITS 64
falign equ 1000h ; section file position modulo
imageBase equ 400000h
; MZ header
DOSHDR:
db 0x4D, 0x5A, 0x90, 0,
dd 3, 4, 0xFFFF, 0xB8, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0
dd PEHDR
db 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
ALIGN falign, db 33h
doshdrSize equ $ - DOSHDR
MetaM: ; MetaBlk for module M
msg db "Hello, Ann!", 0
title db "Hello, Anna!", 0
titlew dw 42Fh, 44Ah, 0
msgw dw 416h, 42Bh, 0
title2w dw 44Ah, 42Fh, 0
ALIGN 8, db 0FEh
MessageBoxA dq 0
MessageBoxW dq 0
ExitProcess dq 0
MessageBoxW0 dq 0 ; a duplicate entry for User32.MessageBoxW
ALIGN falign, db 11h
metamSize equ $ - MetaM
CodeM:
BEGIN:
ENTRY:
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msg] ; LPCSTR lpText
lea r8, [imageBase + title] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
mov rax, [imageBase + MessageBoxA]
call rax
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msgw] ; LPCSTR lpText
lea r8, [imageBase + titlew] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call [imageBase + MessageBoxW]
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msgw] ; LPCSTR lpText
lea r8, [imageBase + title2w] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call [imageBase + MessageBoxW0]
mov ecx, eax
call [imageBase + ExitProcess]
END:
ALIGN falign, db 0AAh
codemSize equ $ - CodeM
IMPORTS:
; DLL names - iterate modules
user32dll db "USER32.DLL", 0
kernel32dll db "KERNEL32.DLL", 0
; Hint/Name entry - iterate externals
MessageBoxA_:
dq MessageBoxA__
dq 0
MessageBoxA__ db 0, 0, "MessageBoxA", 0,
ExitProcess_ dq ExitProcess__
dq 0
ExitProcess__ db 0, 0, "ExitProcess", 0, 1
MessageBoxW_:
dq MessageBoxW__
dq 0
MessageBoxW__ db 0, 0, "MessageBoxW", 0
ImportsDir:
; So this is the Directory, with one entry NOT for every imported DLL,
; but rather one entry for every use of an external name by a CP module
; that is, if a name is used in N modules, it will have N entries in the directory
dd MessageBoxA_, 0, 0, user32dll, MessageBoxA
dd ExitProcess_, 0, 0, kernel32dll, ExitProcess
dd MessageBoxW_, 0, 0, user32dll, MessageBoxW0
dd MessageBoxW_, 0, 0, user32dll, MessageBoxW
dd 0, 0, 0, 0, 0
directorySize equ $ - ImportsDir
importsSize equ $ - IMPORTS
PEHDR:
db "PE", 0, 0 ; signature
dw 8664h ; machine
dw 3 ; # of sections
dd 0 ; timedatestamp
dd 0 ; pointer to symtab - deprecated
dd 0 ; # symtab entries
dw opthdrSize ; size of optional header
dw 203h ; flags - characteristics
OPTHDR:
dw 20Bh ; magic
db 0 ; maj linker ver
db 1 ; minor linker ver
dd codemSize ; total code size
dd metamSize ; total init data size
dd 0 ; total uninit data size
dd ENTRY ; entrypoint RVA
dd ENTRY ; base of code
dq imageBase ; image base
dd 1000h ; section address alignment
dd falign ; section pos alignment
dw 5 ; major OS version
dw 2 ; minor OS version
dw 0 ; major image ver
dw 1 ; minor image ver
dw 5 ; major subsystem ver
dw 2 ; minor subsystem ver
dd 0 ; win32 version value = 0
dd fileSize ; size of image - that is, in memory!
dd ((doshdrSize + pehdrSize) + falign - 1) / falign * falign
; size of headers
dd 0 ; checksum
dw 2 ; subsystem: GUI = 2, CUI =3, NATIVE = 1
dw 0 ; dll characteristics
dq 1000000h ; max stack
dq 1000h ; min stack
dq 1000000h ; max heap
dq 1000h ; min heap
dd 0 ; loader flag = 0
; Directories
dd 2 ; number of directories
; export table hdr
dd 0, 0
; import table hdr
dd ImportsDir ; addr of import table
dd directorySize ; size of import table
;times 14 dq 0 ; end of directories
opthdrSize equ $ - OPTHDR
pehdrSize equ $ - PEHDR
Sections:
; MetaM
db "F", 0, 0, 0, 0 ; null name
dd metamSize ; size
dd MetaM ; addr RVA
dd metamSize ; length
dd MetaM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0C0000040h ; flags: datasection writeable readable
; CodeM
db "W", 0 ; null name
dd codemSize ; size
dd CodeM ; addr RVA
dd codemSize ; length
dd CodeM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
; IMPORTS
db ".idata", 0, 0
dd importsSize ; size
dd IMPORTS ; addr RVA
dd importsSize ; length
dd IMPORTS ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
fileSize equ $
;END:
这里有很多问题。您说此代码在 wine 上运行的事实表明 wine 非常 宽容。 Windows?没那么多。
首先,这是我正在使用的构建命令(基于上面 OP 的原始代码):nasm.exe org.asm -o org.exe
使用 dumpbin(来自 VS2019)对 org.exe 给我们:
File Type: EXECUTABLE IMAGE
org.exe : fatal error LNK1107: invalid or corrupt file: cannot read at 0x31DE
不是一个有希望的开始。我做的第一件事(有所作为)是在 Sections:
中更改此代码
; MetaM
db "F", 0, 0, 0, 0 ; null name
...
; CodeM
db "W", 0 ; null name
根据规范,这些应该是 8 个字节长,而不仅仅是以 null 结尾的字符串。更改了这些,现在 dumpbin 给我:
File Type: EXECUTABLE IMAGE
Summary
1000 .idata
1000 CodeM
1000 MetaM
更好。我的下一步是 dumpbin /headers a.exe
,这给了我:
LINK : fatal error LNK1000: Internal error during ReadOptionalHeader
这是通过取消注释目录下的行修复的:times 14 dq 0
.
我不会 post 整个 dumpbin 输出,但足以说明它现在显示所有部分的 headers。
接下来要看 dumpbin /imports a.exe
。不是将每个 dll 的所有导入分组,而是在此处为每个导入分配了自己的部分。那是不对的。所以我修复了 iData 部分。我也修复了对齐问题。可能还有许多其他问题 需要 修复,但至少现在可以运行了:
BITS 64
falign equ 200h ; section file position modulo
imageBase equ 400000h
; MZ header
DOSHDR:
db 0x4D, 0x5A, 0x90, 0,
dd 3, 4, 0xFFFF, 0xB8, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0
dd PEHDR
db 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
ALIGN falign, db 33h
doshdrSize equ $ - DOSHDR
MetaM: ; MetaBlk for module M
msg db "Hello, Ann!", 0
title db "Hello, Anna!", 0
titlew dw 42Fh, 44Ah, 0
msgw dw 416h, 42Bh, 0
title2w dw 44Ah, 42Fh, 0
ALIGN 8, db 0FEh
ALIGN falign, db 11h
metamSize equ $ - MetaM
CodeM:
BEGIN:
ENTRY:
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msg] ; LPCSTR lpText
lea r8, [imageBase + title] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
mov rax, [imageBase + MessageBoxA]
call rax
mov ecx, eax
call [imageBase + ExitProcess]
END:
ALIGN falign, db 0AAh
codemSize equ $ - CodeM
;========
IMPORTS:
; Import Address Table
ExitProcess dq ExitProcess__
MessageBoxA dq MessageBoxA__
MessageBoxW dq MessageBoxW__
dq 0
ImportsDir:
dd ExitProcess_, 0, 0, kernel32dll, ExitProcess
dd MessageBoxA_, 0, 0, user32dll, MessageBoxA
dd 0, 0, 0, 0, 0
directorySize equ $ - ImportsDir
; Import Lookup Table
ExitProcess_ dq ExitProcess__
dq 0
; Hint/Name entry - iterate externals
ExitProcess__ db 64h, 1, "ExitProcess", 0
dq 0
MessageBoxA_ dq MessageBoxA__
MessageBoxW_ dq MessageBoxW__
dq 0
MessageBoxA__ db 0, 0, "MessageBoxA", 0
MessageBoxW__ db 0, 0, "MessageBoxW", 0
dq 0
kernel32dll db "KERNEL32.dll", 0
user32dll db "USER32.dll", 0
importsSize equ $ - IMPORTS
;========
ALIGN 16
PEHDR:
db "PE", 0, 0 ; signature
dw 8664h ; machine
dw 3 ; # of sections
dd 0 ; timedatestamp
dd 0 ; pointer to symtab - deprecated
dd 0 ; # symtab entries
dw opthdrSize ; size of optional header
dw 203h ; flags - characteristics
OPTHDR:
dw 20Bh ; magic
db 0 ; maj linker ver
db 1 ; minor linker ver
dd codemSize ; total code size
dd metamSize ; total init data size
dd 0 ; total uninit data size
dd ENTRY ; entrypoint RVA
dd ENTRY ; base of code
dq imageBase ; image base
dd falign ; section address alignment
dd falign ; section pos alignment
dw 5 ; major OS version
dw 2 ; minor OS version
dw 0 ; major image ver
dw 1 ; minor image ver
dw 5 ; major subsystem ver
dw 2 ; minor subsystem ver
dd 0 ; win32 version value = 0
dd fileSize ; size of image - that is, in memory!
dd ((doshdrSize + pehdrSize) + falign - 1) / falign * falign
; size of headers
dd 0 ; checksum
dw 2 ; subsystem: GUI = 2, CUI =3, NATIVE = 1
dw 0 ; dll characteristics
dq 1000000h ; max stack
dq 1000h ; min stack
dq 1000000h ; max heap
dq 1000h ; min heap
dd 0 ; loader flag = 0
; Directories
dd 16 ; number of directories
; export table hdr
dd 0, 0
; import table hdr
dd ImportsDir ; addr of import table
dd directorySize ; size of import table
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd ExitProcess, 8 * 3
dd 0, 0
dd 0, 0
dd 0, 0
opthdrSize equ $ - OPTHDR
pehdrSize equ $ - PEHDR
Sections:
; MetaM
db "MetaM", 0, 0, 0 ; null name
dd metamSize ; size
dd MetaM ; addr RVA
dd metamSize ; length
dd MetaM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0C0000040h ; flags: datasection writeable readable
; CodeM
db "CodeM", 0, 0, 0 ; null name
dd codemSize ; size
dd CodeM ; addr RVA
dd codemSize ; length
dd CodeM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
; IMPORTS
db ".idata", 0, 0
dd importsSize ; size
dd IMPORTS ; addr RVA
dd importsSize ; length
dd IMPORTS ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
fileSize equ $
;END:
现在它开始工作了,我会把清理工作留给你。告诉安我打招呼...
更新:
所以我做了一些清理。
- 更多文档
- 更少的 hard-coded 值(尽可能计算)
- 在文件中使用 512 字节对齐(较小的图像大小),同时仍使用 4k 页面(以允许页面保护)。因此,不同的字段使用不同的偏移量。
FWIW
; Check for NASM version at least 2.15.05
%if __?NASM_VERSION_ID?__ < 0x0020F0500
%error "Newer version of nasm required"
%endif
%define RoundTo(a, b) ((((a) + ((b) - 1)) / (b)) * (b))
%define Stringify(&val) val
%macro NameEntry 2
%1__ dw %2
db Stringify(%1), 0
%endmacro
salign equ 1000h ; Page size in memory
falign equ 200h ; Page size in file
imageBase equ 400000h ; Requested load address
BITS 16
section headers start=0
startoffile:
; MZ header https://wiki.osdev.org/MZ
dw "MZ" ; Signature
dw (dosBlkSize - mzStructSize) % 512 ; Bytes on last page
dw RoundTo(dosBlkSize, 512) / 512 ; # of 512 byte pages
dw 0 ; Relocation items
dw RoundTo(mzStructSize, 16) / 16 ; Header size in paragraphs
dw 0 ; Minimum allocation
dw 0xffff ; Maximum allocation in paragraphs (1M).
dw 0 ; Initial SS
dw 0xb8 ; Initial SP
dw 0 ; Checksum
dw 0 ; Initial IP
dw 0 ; Initial CS
dw 0 ; Relocation table
dw 0 ; Overlay
dq 0 ; Reserved
dw 0 ; OEM identifier
dw 0 ; OEM info
times 20 db 0 ; Reserved
dd PEHDR ; PE header start
mzStructSize equ $ - $$ ; aka 64
dosstartcode: ; Print the error and exit
push cs
pop ds
mov dx, dosmsg - dosstartcode
mov ah, 0x9
int 0x21 ; Show string up to '$'
mov ax, 4c01h
int 0x21 ; Exit process with error code 1
dosmsg db `This program cannot be run in DOS mode.\r\r\n$`
dosBlkSize equ $ - $$
ALIGN 16
; From https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
PEHDR:
dd "PE" ; signature
dw 8664h ; machine x64
dw SectionsCount ; # of sections
dd __POSIX_TIME__ ; timedatestamp
dd 0 ; pointer to symtab - deprecated
dd 0 ; # symtab entries
dw opthdrSize ; size of optional header
dw 2h ; flags: Executable
OPTHDR:
dw 20Bh ; magic
db 0 ; maj linker ver
db 0 ; minor linker ver
dd codeSizeS ; total memory code size
dd rdataSizeS ; total memory init data size
dd 0 ; total uninit data size
dd ENTRY ; entrypoint RVA
dd section..text.start ; base of code in file
dq imageBase ; image base
dd salign ; section address alignment
dd falign ; section pos alignment
dw 10 ; major OS version
dw 0 ; minor OS version
dw 0 ; major image ver
dw 1 ; minor image ver
dw 6 ; major subsystem ver
dw 2 ; minor subsystem ver
dd 0 ; win32 version value = 0
dd fileSize ; size of image in memory
dd headersSizeF ; size of DOS stub + PE header + sections
dd 0 ; checksum
dw 2 ; subsystem: GUI
dw 8160h ; dll characteristics: HighEntropy, Relocatable, NX, TS aware
dq 100h ; max stack
dq 100h ; min stack
dq 100h ; max heap
dq 100h ; min heap
dd 0 ; loader flag
HeaderDirectories:
dd HeaderDirectoryCount ; number of directories
; Address, Size
dd 0, 0 ; Export
dd ImportsDir, ImportsDirSize ; Import
dd 0, 0 ; Resource
dd 0, 0 ; Exception
dd 0, 0 ; Certificates
dd 0, 0 ; Base Relocation
dd 0, 0 ; Debug
dd 0, 0 ; Architecture
dd 0, 0 ; Global Pointer
dd 0, 0 ; Thread Storage
dd 0, 0 ; Load Configuration
dd 0, 0 ; Bound Import
dd IATStart, IATSize ; Import Address Table
dd 0, 0 ; Delay Import
dd 0, 0 ; COM Descriptor
dd 0, 0 ; Reserved
HeaderDirectorySize equ $ - HeaderDirectories
HeaderDirectoryCount equ HeaderDirectorySize / 8
opthdrSize equ $ - OPTHDR
startOfSections:
dq ".text"
dd codeSizeS ; size in memory pages
dd ENTRY ; addr RVA (memory offset)
dd codeSize ; length
dd section..text.start ; pos (file offset)
dd 0 ; relocations addr
dd 0 ; linenum addr
dw 0 ; relocations count
dw 0 ; linenum count
dd 030000020h ; flags: Code, Shared, Execute Only
dq ".rdata"
dd rdataSizeS ; size in memory pages
dd RDATA ; addr RVA (memory offset)
dd rdataSize ; length
dd section.rdata.start ; pos (file offset)
dd 0 ; relocations addr
dd 0 ; linenum addr
dw 0 ; relocations count
dw 0 ; linenum count
; Take advantage of the fact that the loader cheats and
; writes imports to readonly pages @ startup
dd 040000040h ; flags: Initialized Data, Read Only
SectionsSize equ $ - startOfSections
SectionsCount equ SectionsSize / 40
ALIGN 16
headersSizeF equ RoundTo($ - $$, falign)
headersSizeS equ RoundTo($ - $$, salign)
BITS 64
DEFAULT REL ; so we don't have to keep adding imageBase
SECTION .text vstart=headersSizeS align=falign follows=headers
ENTRY:
sub rsp, 28h
xor ecx, ecx ; hWnd = HWND_DESKTOP
lea rdx, [msg] ; LPCSTR lpText
lea r8, [title] ; LPCSTR lpCaption
xor r9d, r9d ; uType = MB_OK
call [MessageBoxA]
; The return value from MessageBoxA may not be what you think
mov ecx, eax
call [ExitProcess]
codeSize equ $ - $$
codeSizeS equ RoundTo(codeSize, salign)
SECTION rdata vstart=headersSizeS+codeSizeS align=falign
RDATA:
IATStart:
; Import Address Table
Kernel32TableA:
ExitProcess dq ExitProcess__
User32TableA:
MessageBoxA dq MessageBoxA__
MessageBoxW dq MessageBoxW__
IATSize equ $ - IATStart
ImportsDir:
dd Kernel32TableL, 0, 0, kernel32dll, Kernel32TableA
dd User32TableL, 0, 0, user32dll, User32TableA
ImportsDirSize equ $ - ImportsDir
; Kernel32 Import Lookup Table
Kernel32TableL:
dq ExitProcess__
dq 0 ; end of table marker
; Name, Hint
NameEntry ExitProcess, 164h
; User32 Import Lookup Table
User32TableL:
dq MessageBoxA__
dq MessageBoxW__
dq 0 ; end of table marker
; Name, Hint
NameEntry MessageBoxA, 28fh
NameEntry MessageBoxW, 28ch
kernel32dll db "KERNEL32.dll", 0
user32dll db "USER32.dll", 0
; Constant data
msg db "Hello, Ann!", 0
title db "Hello, Anna!", 0
ALIGN 16
rdataSize equ $ - RDATA
rdataSizeS equ RoundTo(rdataSize, salign)
fileSize equ RDATA + rdataSizeS
我想要 post 可以在 Wine 和 Windows 10 中使用的最终版本。简而言之,这是错误的地方:
- 图像大小字段未对齐。必须是 page-aligned (1000h)。我的错。
- 计算错误 headers 尺寸。规格不佳。这帮助我弄明白了,而不是规范:PE format walkthru
- 有 2 个目录是行不通的,我认为这与规范不矛盾。我没有用 16 以外的数字进行实验。规格不佳。顺便说一下,目录中的 IAT 是无关紧要的;实际上,如果您导入 2 个 DLL,每个都会获取 IAT;目录中应引用哪一个?答:装载机根本不在乎。
- 一个IAT的第一个条目必须是non-zero,否则这个IAT将被忽略而不填写。这是非常没有记录的。规格不佳。
积极的一面,
- 我能够将 PE header 和节列表放在文件末尾,这与通常将其放在 DOS 存根之后和节之前不同。
- 我能够以一种有点奇怪的方式组织导入,每个导入的符号一个 IAT,而不是每个 DLL 一个 IAT。
在这两个问题上,我和加载程序都遵循规范的字母,这是一件好事。
BITS 64
; nasm -f bin -o pe.exe pe.asm && chmod +x pe.exe && ./pe.exe
salign equ 1000h ; section file position modulo
falign equ 1000h ; section file position modulo
imageBase equ 400000h
; MZ header
DOSHDR:
db 0x4D, 0x5A, 0x90, 0,
dd 3, 4, 0xFFFF, 0xB8, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0
dd PEHDR
db 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
ALIGN 8, db 0FFh
doshdrSize equ $ - DOSHDR
ALIGN falign, db 55h
MetaM: ; MetaBlk for module M
msg db "Hello, Ann!", 0
title db "Hello, Anna!", 0
titlew dw 42Fh, 44Ah, 0
msgw dw 416h, 42Bh, 0
title2w dw 44Ah, 42Fh, 0
ALIGN 8, db 0FEh
MessageBoxA dq 01
MessageBoxW dq 01
ExitProcess dq 01
MessageBoxW0 dq 01 ; a duplicate entry for User32.MessageBoxW
ALIGN falign, db 11h
metamSize equ $ - MetaM
CodeM:
BEGIN:
ENTRY:
; for PROXIES instead of IAT
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msg] ; LPCSTR lpText
lea r8, [imageBase + title] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
mov rax, [imageBase + MessageBoxA]
call rax
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msgw] ; LPCSTR lpText
lea r8, [imageBase + titlew] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call [imageBase + MessageBoxW]
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msgw] ; LPCSTR lpText
lea r8, [imageBase + title2w] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call [imageBase + MessageBoxW]
mov ecx, eax
call [imageBase + ExitProcess]
END:
ALIGN falign, db 0AAh
codemSize equ $ - CodeM
IMPORTS:
ImportsDir:
; So this is the Directory, with one entry NOT for every imported DLL,
; but rather one entry for every use of an external name by a CP module
; that is, if a name is used in N modules, it will have N entries in the directory
dd MessageBoxA_, 0, 0, user32dll, MessageBoxA
dd ExitProcess_, 0, 0, kernel32dll, ExitProcess
dd MessageBoxW_, 0, 0, user32dll, MessageBoxW0
dd MessageBoxW_, 0, 0, user32dll, MessageBoxW
dd 0, 0, 0, 0, 0
directorySize equ $ - ImportsDir
; DLL names - iterate modules
user32dll db "USER32.DLL", 0
kernel32dll db "KERNEL32.DLL", 0
; Hint/Name entry - iterate externals
MessageBoxA_:
dq MessageBoxA__
dq 0
MessageBoxA__ db 0, 0, "MessageBoxA", 0
ExitProcess_ dq ExitProcess__
dq 0
ExitProcess__ db 0, 0, "ExitProcess", 0
MessageBoxW_:
dq MessageBoxW__
dq 0
MessageBoxW__ db 0, 0, "MessageBoxW", 0
importsSize equ $ - IMPORTS
ALIGN 8, db 99h
PEHDR:
db "PE", 0, 0 ; signature
dw 8664h ; machine
dw 3 ; # of sections
dd 0 ; timedatestamp
dd 0 ; pointer to symtab - deprecated
dd 0 ; # symtab entries
dw opthdrSize ; size of optional header
dw 203h ; flags - characteristics
OPTHDR:
dw 20Bh ; magic
db 0 ; maj linker ver
db 1 ; minor linker ver
dd codemSize ; total code size
dd metamSize ; total init data size
dd 0 ; total uninit data size
dd ENTRY ; entrypoint RVA
dd ENTRY ; base of code
dq imageBase ; image base
dd 1000h ; section address alignment
dd falign ; section pos alignment
dw 5 ; major OS version
dw 1 ; minor OS version
dw 0 ; major image ver
dw 1 ; minor image ver
dw 5 ; major subsystem ver
dw 0 ; minor subsystem ver
dd 0 ; win32 version value = 0
dd 4000h ;(*(fileSize + salign - 1) / salign * salign*)
; imageSize - that is, in memory!
dd salign
; size of headers
dd 0 ; checksum
dw 2 ; subsystem: GUI = 2, CUI =3, NATIVE = 1
dw 0 ; dll characteristics
dq 1000000h ; max stack
dq 1000h ; min stack
dq 1000000h ; max heap
dq 1000h ; min heap
dd 0 ; loader flag = 0
; Directories
dd 16 ; number of directories
; export table hdr
dd 0, 0
; import table hdr
dd ImportsDir ; addr of import table
dd directorySize ; size of import table
times 14 dd 0, 0 ; empty directories
; dd kernel32IAT ; IATs
; dd 5 * 8
;times 3 dd 0, 0 ; empty directories
opthdrSize equ $ - OPTHDR
pehdrSize equ $ - PEHDR
Sections:
; MetaM
db "F***", 0, 0, 0, 0 ; null name
dd metamSize ; size
dd MetaM ; addr RVA
dd metamSize ; length
dd MetaM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0C0000040h ; flags: datasection writeable readable
; CodeM
db "Windows", 0 ; null name
dd codemSize ; size
dd CodeM ; addr RVA
dd codemSize ; length
dd CodeM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
; IMPORTS
db ".idata", 0, 0
dd importsSize ; size
dd IMPORTS ; addr RVA
dd importsSize ; length
dd IMPORTS ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
fileSize equ $
;END.
P.S。我觉得很奇怪,编程中的一个事实上的标准是使用颜色来突出显示语法,而不是使用颜色来突出显示含义。就此而言,甚至 boldface/italics 也不行。所以,我希望我可以将源代码中的关键部分着色或加粗 - 唉,这是不可能的。在我的编程环境 - BlackBox Component Builder - 我可以自由使用颜色和 bold/italic 我想要的一切:
我已经(在汇编器中,没有链接器)为 x86-64 制作了一个 EXE,它在 Linux 下的 Wine 中运行得非常好。这是调用 MessageBoxA 和 ExitProcess 的基本 HelloWorld。
Windows10不会认,说'This program cannot be executed on your computer, talk to your vendor for a version that will suit your computer'.
我使用PE 格式阅读器(PE 工具和CFF 资源管理器)来分析我的PE EXE。 PE Optional header 中的所有数字与其他工作 EXE 中的数字相同(如 os 版本、子系统版本)。只有特定于我的文件和部分的那些不同。而且 Windows 不会将该文件识别为我的计算机上的可执行文件。
除了 WIndows 错误消息,我还能从哪里开始查看?是否有任何工具可以使用比 'Bad exe' 更具体的错误消息来检查 EXE 有效性? (这是 xdbg 报告的内容。
在 Wine 上,我能够做到
WINEDEBUG=+all wine my.exe
这给了我错误的提示,我能够修复它并让它工作。 Windows 中有这样的工具吗?
BITS 64
falign equ 1000h ; section file position modulo
imageBase equ 400000h
; MZ header
DOSHDR:
db 0x4D, 0x5A, 0x90, 0,
dd 3, 4, 0xFFFF, 0xB8, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0
dd PEHDR
db 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
ALIGN falign, db 33h
doshdrSize equ $ - DOSHDR
MetaM: ; MetaBlk for module M
msg db "Hello, Ann!", 0
title db "Hello, Anna!", 0
titlew dw 42Fh, 44Ah, 0
msgw dw 416h, 42Bh, 0
title2w dw 44Ah, 42Fh, 0
ALIGN 8, db 0FEh
MessageBoxA dq 0
MessageBoxW dq 0
ExitProcess dq 0
MessageBoxW0 dq 0 ; a duplicate entry for User32.MessageBoxW
ALIGN falign, db 11h
metamSize equ $ - MetaM
CodeM:
BEGIN:
ENTRY:
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msg] ; LPCSTR lpText
lea r8, [imageBase + title] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
mov rax, [imageBase + MessageBoxA]
call rax
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msgw] ; LPCSTR lpText
lea r8, [imageBase + titlew] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call [imageBase + MessageBoxW]
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msgw] ; LPCSTR lpText
lea r8, [imageBase + title2w] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call [imageBase + MessageBoxW0]
mov ecx, eax
call [imageBase + ExitProcess]
END:
ALIGN falign, db 0AAh
codemSize equ $ - CodeM
IMPORTS:
; DLL names - iterate modules
user32dll db "USER32.DLL", 0
kernel32dll db "KERNEL32.DLL", 0
; Hint/Name entry - iterate externals
MessageBoxA_:
dq MessageBoxA__
dq 0
MessageBoxA__ db 0, 0, "MessageBoxA", 0,
ExitProcess_ dq ExitProcess__
dq 0
ExitProcess__ db 0, 0, "ExitProcess", 0, 1
MessageBoxW_:
dq MessageBoxW__
dq 0
MessageBoxW__ db 0, 0, "MessageBoxW", 0
ImportsDir:
; So this is the Directory, with one entry NOT for every imported DLL,
; but rather one entry for every use of an external name by a CP module
; that is, if a name is used in N modules, it will have N entries in the directory
dd MessageBoxA_, 0, 0, user32dll, MessageBoxA
dd ExitProcess_, 0, 0, kernel32dll, ExitProcess
dd MessageBoxW_, 0, 0, user32dll, MessageBoxW0
dd MessageBoxW_, 0, 0, user32dll, MessageBoxW
dd 0, 0, 0, 0, 0
directorySize equ $ - ImportsDir
importsSize equ $ - IMPORTS
PEHDR:
db "PE", 0, 0 ; signature
dw 8664h ; machine
dw 3 ; # of sections
dd 0 ; timedatestamp
dd 0 ; pointer to symtab - deprecated
dd 0 ; # symtab entries
dw opthdrSize ; size of optional header
dw 203h ; flags - characteristics
OPTHDR:
dw 20Bh ; magic
db 0 ; maj linker ver
db 1 ; minor linker ver
dd codemSize ; total code size
dd metamSize ; total init data size
dd 0 ; total uninit data size
dd ENTRY ; entrypoint RVA
dd ENTRY ; base of code
dq imageBase ; image base
dd 1000h ; section address alignment
dd falign ; section pos alignment
dw 5 ; major OS version
dw 2 ; minor OS version
dw 0 ; major image ver
dw 1 ; minor image ver
dw 5 ; major subsystem ver
dw 2 ; minor subsystem ver
dd 0 ; win32 version value = 0
dd fileSize ; size of image - that is, in memory!
dd ((doshdrSize + pehdrSize) + falign - 1) / falign * falign
; size of headers
dd 0 ; checksum
dw 2 ; subsystem: GUI = 2, CUI =3, NATIVE = 1
dw 0 ; dll characteristics
dq 1000000h ; max stack
dq 1000h ; min stack
dq 1000000h ; max heap
dq 1000h ; min heap
dd 0 ; loader flag = 0
; Directories
dd 2 ; number of directories
; export table hdr
dd 0, 0
; import table hdr
dd ImportsDir ; addr of import table
dd directorySize ; size of import table
;times 14 dq 0 ; end of directories
opthdrSize equ $ - OPTHDR
pehdrSize equ $ - PEHDR
Sections:
; MetaM
db "F", 0, 0, 0, 0 ; null name
dd metamSize ; size
dd MetaM ; addr RVA
dd metamSize ; length
dd MetaM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0C0000040h ; flags: datasection writeable readable
; CodeM
db "W", 0 ; null name
dd codemSize ; size
dd CodeM ; addr RVA
dd codemSize ; length
dd CodeM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
; IMPORTS
db ".idata", 0, 0
dd importsSize ; size
dd IMPORTS ; addr RVA
dd importsSize ; length
dd IMPORTS ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
fileSize equ $
;END:
这里有很多问题。您说此代码在 wine 上运行的事实表明 wine 非常 宽容。 Windows?没那么多。
首先,这是我正在使用的构建命令(基于上面 OP 的原始代码):nasm.exe org.asm -o org.exe
使用 dumpbin(来自 VS2019)对 org.exe 给我们:
File Type: EXECUTABLE IMAGE
org.exe : fatal error LNK1107: invalid or corrupt file: cannot read at 0x31DE
不是一个有希望的开始。我做的第一件事(有所作为)是在 Sections:
; MetaM
db "F", 0, 0, 0, 0 ; null name
...
; CodeM
db "W", 0 ; null name
根据规范,这些应该是 8 个字节长,而不仅仅是以 null 结尾的字符串。更改了这些,现在 dumpbin 给我:
File Type: EXECUTABLE IMAGE
Summary
1000 .idata
1000 CodeM
1000 MetaM
更好。我的下一步是 dumpbin /headers a.exe
,这给了我:
LINK : fatal error LNK1000: Internal error during ReadOptionalHeader
这是通过取消注释目录下的行修复的:times 14 dq 0
.
我不会 post 整个 dumpbin 输出,但足以说明它现在显示所有部分的 headers。
接下来要看 dumpbin /imports a.exe
。不是将每个 dll 的所有导入分组,而是在此处为每个导入分配了自己的部分。那是不对的。所以我修复了 iData 部分。我也修复了对齐问题。可能还有许多其他问题 需要 修复,但至少现在可以运行了:
BITS 64
falign equ 200h ; section file position modulo
imageBase equ 400000h
; MZ header
DOSHDR:
db 0x4D, 0x5A, 0x90, 0,
dd 3, 4, 0xFFFF, 0xB8, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0
dd PEHDR
db 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
ALIGN falign, db 33h
doshdrSize equ $ - DOSHDR
MetaM: ; MetaBlk for module M
msg db "Hello, Ann!", 0
title db "Hello, Anna!", 0
titlew dw 42Fh, 44Ah, 0
msgw dw 416h, 42Bh, 0
title2w dw 44Ah, 42Fh, 0
ALIGN 8, db 0FEh
ALIGN falign, db 11h
metamSize equ $ - MetaM
CodeM:
BEGIN:
ENTRY:
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msg] ; LPCSTR lpText
lea r8, [imageBase + title] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
mov rax, [imageBase + MessageBoxA]
call rax
mov ecx, eax
call [imageBase + ExitProcess]
END:
ALIGN falign, db 0AAh
codemSize equ $ - CodeM
;========
IMPORTS:
; Import Address Table
ExitProcess dq ExitProcess__
MessageBoxA dq MessageBoxA__
MessageBoxW dq MessageBoxW__
dq 0
ImportsDir:
dd ExitProcess_, 0, 0, kernel32dll, ExitProcess
dd MessageBoxA_, 0, 0, user32dll, MessageBoxA
dd 0, 0, 0, 0, 0
directorySize equ $ - ImportsDir
; Import Lookup Table
ExitProcess_ dq ExitProcess__
dq 0
; Hint/Name entry - iterate externals
ExitProcess__ db 64h, 1, "ExitProcess", 0
dq 0
MessageBoxA_ dq MessageBoxA__
MessageBoxW_ dq MessageBoxW__
dq 0
MessageBoxA__ db 0, 0, "MessageBoxA", 0
MessageBoxW__ db 0, 0, "MessageBoxW", 0
dq 0
kernel32dll db "KERNEL32.dll", 0
user32dll db "USER32.dll", 0
importsSize equ $ - IMPORTS
;========
ALIGN 16
PEHDR:
db "PE", 0, 0 ; signature
dw 8664h ; machine
dw 3 ; # of sections
dd 0 ; timedatestamp
dd 0 ; pointer to symtab - deprecated
dd 0 ; # symtab entries
dw opthdrSize ; size of optional header
dw 203h ; flags - characteristics
OPTHDR:
dw 20Bh ; magic
db 0 ; maj linker ver
db 1 ; minor linker ver
dd codemSize ; total code size
dd metamSize ; total init data size
dd 0 ; total uninit data size
dd ENTRY ; entrypoint RVA
dd ENTRY ; base of code
dq imageBase ; image base
dd falign ; section address alignment
dd falign ; section pos alignment
dw 5 ; major OS version
dw 2 ; minor OS version
dw 0 ; major image ver
dw 1 ; minor image ver
dw 5 ; major subsystem ver
dw 2 ; minor subsystem ver
dd 0 ; win32 version value = 0
dd fileSize ; size of image - that is, in memory!
dd ((doshdrSize + pehdrSize) + falign - 1) / falign * falign
; size of headers
dd 0 ; checksum
dw 2 ; subsystem: GUI = 2, CUI =3, NATIVE = 1
dw 0 ; dll characteristics
dq 1000000h ; max stack
dq 1000h ; min stack
dq 1000000h ; max heap
dq 1000h ; min heap
dd 0 ; loader flag = 0
; Directories
dd 16 ; number of directories
; export table hdr
dd 0, 0
; import table hdr
dd ImportsDir ; addr of import table
dd directorySize ; size of import table
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd 0, 0
dd ExitProcess, 8 * 3
dd 0, 0
dd 0, 0
dd 0, 0
opthdrSize equ $ - OPTHDR
pehdrSize equ $ - PEHDR
Sections:
; MetaM
db "MetaM", 0, 0, 0 ; null name
dd metamSize ; size
dd MetaM ; addr RVA
dd metamSize ; length
dd MetaM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0C0000040h ; flags: datasection writeable readable
; CodeM
db "CodeM", 0, 0, 0 ; null name
dd codemSize ; size
dd CodeM ; addr RVA
dd codemSize ; length
dd CodeM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
; IMPORTS
db ".idata", 0, 0
dd importsSize ; size
dd IMPORTS ; addr RVA
dd importsSize ; length
dd IMPORTS ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
fileSize equ $
;END:
现在它开始工作了,我会把清理工作留给你。告诉安我打招呼...
更新:
所以我做了一些清理。
- 更多文档
- 更少的 hard-coded 值(尽可能计算)
- 在文件中使用 512 字节对齐(较小的图像大小),同时仍使用 4k 页面(以允许页面保护)。因此,不同的字段使用不同的偏移量。
FWIW
; Check for NASM version at least 2.15.05
%if __?NASM_VERSION_ID?__ < 0x0020F0500
%error "Newer version of nasm required"
%endif
%define RoundTo(a, b) ((((a) + ((b) - 1)) / (b)) * (b))
%define Stringify(&val) val
%macro NameEntry 2
%1__ dw %2
db Stringify(%1), 0
%endmacro
salign equ 1000h ; Page size in memory
falign equ 200h ; Page size in file
imageBase equ 400000h ; Requested load address
BITS 16
section headers start=0
startoffile:
; MZ header https://wiki.osdev.org/MZ
dw "MZ" ; Signature
dw (dosBlkSize - mzStructSize) % 512 ; Bytes on last page
dw RoundTo(dosBlkSize, 512) / 512 ; # of 512 byte pages
dw 0 ; Relocation items
dw RoundTo(mzStructSize, 16) / 16 ; Header size in paragraphs
dw 0 ; Minimum allocation
dw 0xffff ; Maximum allocation in paragraphs (1M).
dw 0 ; Initial SS
dw 0xb8 ; Initial SP
dw 0 ; Checksum
dw 0 ; Initial IP
dw 0 ; Initial CS
dw 0 ; Relocation table
dw 0 ; Overlay
dq 0 ; Reserved
dw 0 ; OEM identifier
dw 0 ; OEM info
times 20 db 0 ; Reserved
dd PEHDR ; PE header start
mzStructSize equ $ - $$ ; aka 64
dosstartcode: ; Print the error and exit
push cs
pop ds
mov dx, dosmsg - dosstartcode
mov ah, 0x9
int 0x21 ; Show string up to '$'
mov ax, 4c01h
int 0x21 ; Exit process with error code 1
dosmsg db `This program cannot be run in DOS mode.\r\r\n$`
dosBlkSize equ $ - $$
ALIGN 16
; From https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
PEHDR:
dd "PE" ; signature
dw 8664h ; machine x64
dw SectionsCount ; # of sections
dd __POSIX_TIME__ ; timedatestamp
dd 0 ; pointer to symtab - deprecated
dd 0 ; # symtab entries
dw opthdrSize ; size of optional header
dw 2h ; flags: Executable
OPTHDR:
dw 20Bh ; magic
db 0 ; maj linker ver
db 0 ; minor linker ver
dd codeSizeS ; total memory code size
dd rdataSizeS ; total memory init data size
dd 0 ; total uninit data size
dd ENTRY ; entrypoint RVA
dd section..text.start ; base of code in file
dq imageBase ; image base
dd salign ; section address alignment
dd falign ; section pos alignment
dw 10 ; major OS version
dw 0 ; minor OS version
dw 0 ; major image ver
dw 1 ; minor image ver
dw 6 ; major subsystem ver
dw 2 ; minor subsystem ver
dd 0 ; win32 version value = 0
dd fileSize ; size of image in memory
dd headersSizeF ; size of DOS stub + PE header + sections
dd 0 ; checksum
dw 2 ; subsystem: GUI
dw 8160h ; dll characteristics: HighEntropy, Relocatable, NX, TS aware
dq 100h ; max stack
dq 100h ; min stack
dq 100h ; max heap
dq 100h ; min heap
dd 0 ; loader flag
HeaderDirectories:
dd HeaderDirectoryCount ; number of directories
; Address, Size
dd 0, 0 ; Export
dd ImportsDir, ImportsDirSize ; Import
dd 0, 0 ; Resource
dd 0, 0 ; Exception
dd 0, 0 ; Certificates
dd 0, 0 ; Base Relocation
dd 0, 0 ; Debug
dd 0, 0 ; Architecture
dd 0, 0 ; Global Pointer
dd 0, 0 ; Thread Storage
dd 0, 0 ; Load Configuration
dd 0, 0 ; Bound Import
dd IATStart, IATSize ; Import Address Table
dd 0, 0 ; Delay Import
dd 0, 0 ; COM Descriptor
dd 0, 0 ; Reserved
HeaderDirectorySize equ $ - HeaderDirectories
HeaderDirectoryCount equ HeaderDirectorySize / 8
opthdrSize equ $ - OPTHDR
startOfSections:
dq ".text"
dd codeSizeS ; size in memory pages
dd ENTRY ; addr RVA (memory offset)
dd codeSize ; length
dd section..text.start ; pos (file offset)
dd 0 ; relocations addr
dd 0 ; linenum addr
dw 0 ; relocations count
dw 0 ; linenum count
dd 030000020h ; flags: Code, Shared, Execute Only
dq ".rdata"
dd rdataSizeS ; size in memory pages
dd RDATA ; addr RVA (memory offset)
dd rdataSize ; length
dd section.rdata.start ; pos (file offset)
dd 0 ; relocations addr
dd 0 ; linenum addr
dw 0 ; relocations count
dw 0 ; linenum count
; Take advantage of the fact that the loader cheats and
; writes imports to readonly pages @ startup
dd 040000040h ; flags: Initialized Data, Read Only
SectionsSize equ $ - startOfSections
SectionsCount equ SectionsSize / 40
ALIGN 16
headersSizeF equ RoundTo($ - $$, falign)
headersSizeS equ RoundTo($ - $$, salign)
BITS 64
DEFAULT REL ; so we don't have to keep adding imageBase
SECTION .text vstart=headersSizeS align=falign follows=headers
ENTRY:
sub rsp, 28h
xor ecx, ecx ; hWnd = HWND_DESKTOP
lea rdx, [msg] ; LPCSTR lpText
lea r8, [title] ; LPCSTR lpCaption
xor r9d, r9d ; uType = MB_OK
call [MessageBoxA]
; The return value from MessageBoxA may not be what you think
mov ecx, eax
call [ExitProcess]
codeSize equ $ - $$
codeSizeS equ RoundTo(codeSize, salign)
SECTION rdata vstart=headersSizeS+codeSizeS align=falign
RDATA:
IATStart:
; Import Address Table
Kernel32TableA:
ExitProcess dq ExitProcess__
User32TableA:
MessageBoxA dq MessageBoxA__
MessageBoxW dq MessageBoxW__
IATSize equ $ - IATStart
ImportsDir:
dd Kernel32TableL, 0, 0, kernel32dll, Kernel32TableA
dd User32TableL, 0, 0, user32dll, User32TableA
ImportsDirSize equ $ - ImportsDir
; Kernel32 Import Lookup Table
Kernel32TableL:
dq ExitProcess__
dq 0 ; end of table marker
; Name, Hint
NameEntry ExitProcess, 164h
; User32 Import Lookup Table
User32TableL:
dq MessageBoxA__
dq MessageBoxW__
dq 0 ; end of table marker
; Name, Hint
NameEntry MessageBoxA, 28fh
NameEntry MessageBoxW, 28ch
kernel32dll db "KERNEL32.dll", 0
user32dll db "USER32.dll", 0
; Constant data
msg db "Hello, Ann!", 0
title db "Hello, Anna!", 0
ALIGN 16
rdataSize equ $ - RDATA
rdataSizeS equ RoundTo(rdataSize, salign)
fileSize equ RDATA + rdataSizeS
我想要 post 可以在 Wine 和 Windows 10 中使用的最终版本。简而言之,这是错误的地方:
- 图像大小字段未对齐。必须是 page-aligned (1000h)。我的错。
- 计算错误 headers 尺寸。规格不佳。这帮助我弄明白了,而不是规范:PE format walkthru
- 有 2 个目录是行不通的,我认为这与规范不矛盾。我没有用 16 以外的数字进行实验。规格不佳。顺便说一下,目录中的 IAT 是无关紧要的;实际上,如果您导入 2 个 DLL,每个都会获取 IAT;目录中应引用哪一个?答:装载机根本不在乎。
- 一个IAT的第一个条目必须是non-zero,否则这个IAT将被忽略而不填写。这是非常没有记录的。规格不佳。
积极的一面,
- 我能够将 PE header 和节列表放在文件末尾,这与通常将其放在 DOS 存根之后和节之前不同。
- 我能够以一种有点奇怪的方式组织导入,每个导入的符号一个 IAT,而不是每个 DLL 一个 IAT。 在这两个问题上,我和加载程序都遵循规范的字母,这是一件好事。
BITS 64
; nasm -f bin -o pe.exe pe.asm && chmod +x pe.exe && ./pe.exe
salign equ 1000h ; section file position modulo
falign equ 1000h ; section file position modulo
imageBase equ 400000h
; MZ header
DOSHDR:
db 0x4D, 0x5A, 0x90, 0,
dd 3, 4, 0xFFFF, 0xB8, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0
dd PEHDR
db 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
ALIGN 8, db 0FFh
doshdrSize equ $ - DOSHDR
ALIGN falign, db 55h
MetaM: ; MetaBlk for module M
msg db "Hello, Ann!", 0
title db "Hello, Anna!", 0
titlew dw 42Fh, 44Ah, 0
msgw dw 416h, 42Bh, 0
title2w dw 44Ah, 42Fh, 0
ALIGN 8, db 0FEh
MessageBoxA dq 01
MessageBoxW dq 01
ExitProcess dq 01
MessageBoxW0 dq 01 ; a duplicate entry for User32.MessageBoxW
ALIGN falign, db 11h
metamSize equ $ - MetaM
CodeM:
BEGIN:
ENTRY:
; for PROXIES instead of IAT
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msg] ; LPCSTR lpText
lea r8, [imageBase + title] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
mov rax, [imageBase + MessageBoxA]
call rax
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msgw] ; LPCSTR lpText
lea r8, [imageBase + titlew] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call [imageBase + MessageBoxW]
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, [imageBase + msgw] ; LPCSTR lpText
lea r8, [imageBase + title2w] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call [imageBase + MessageBoxW]
mov ecx, eax
call [imageBase + ExitProcess]
END:
ALIGN falign, db 0AAh
codemSize equ $ - CodeM
IMPORTS:
ImportsDir:
; So this is the Directory, with one entry NOT for every imported DLL,
; but rather one entry for every use of an external name by a CP module
; that is, if a name is used in N modules, it will have N entries in the directory
dd MessageBoxA_, 0, 0, user32dll, MessageBoxA
dd ExitProcess_, 0, 0, kernel32dll, ExitProcess
dd MessageBoxW_, 0, 0, user32dll, MessageBoxW0
dd MessageBoxW_, 0, 0, user32dll, MessageBoxW
dd 0, 0, 0, 0, 0
directorySize equ $ - ImportsDir
; DLL names - iterate modules
user32dll db "USER32.DLL", 0
kernel32dll db "KERNEL32.DLL", 0
; Hint/Name entry - iterate externals
MessageBoxA_:
dq MessageBoxA__
dq 0
MessageBoxA__ db 0, 0, "MessageBoxA", 0
ExitProcess_ dq ExitProcess__
dq 0
ExitProcess__ db 0, 0, "ExitProcess", 0
MessageBoxW_:
dq MessageBoxW__
dq 0
MessageBoxW__ db 0, 0, "MessageBoxW", 0
importsSize equ $ - IMPORTS
ALIGN 8, db 99h
PEHDR:
db "PE", 0, 0 ; signature
dw 8664h ; machine
dw 3 ; # of sections
dd 0 ; timedatestamp
dd 0 ; pointer to symtab - deprecated
dd 0 ; # symtab entries
dw opthdrSize ; size of optional header
dw 203h ; flags - characteristics
OPTHDR:
dw 20Bh ; magic
db 0 ; maj linker ver
db 1 ; minor linker ver
dd codemSize ; total code size
dd metamSize ; total init data size
dd 0 ; total uninit data size
dd ENTRY ; entrypoint RVA
dd ENTRY ; base of code
dq imageBase ; image base
dd 1000h ; section address alignment
dd falign ; section pos alignment
dw 5 ; major OS version
dw 1 ; minor OS version
dw 0 ; major image ver
dw 1 ; minor image ver
dw 5 ; major subsystem ver
dw 0 ; minor subsystem ver
dd 0 ; win32 version value = 0
dd 4000h ;(*(fileSize + salign - 1) / salign * salign*)
; imageSize - that is, in memory!
dd salign
; size of headers
dd 0 ; checksum
dw 2 ; subsystem: GUI = 2, CUI =3, NATIVE = 1
dw 0 ; dll characteristics
dq 1000000h ; max stack
dq 1000h ; min stack
dq 1000000h ; max heap
dq 1000h ; min heap
dd 0 ; loader flag = 0
; Directories
dd 16 ; number of directories
; export table hdr
dd 0, 0
; import table hdr
dd ImportsDir ; addr of import table
dd directorySize ; size of import table
times 14 dd 0, 0 ; empty directories
; dd kernel32IAT ; IATs
; dd 5 * 8
;times 3 dd 0, 0 ; empty directories
opthdrSize equ $ - OPTHDR
pehdrSize equ $ - PEHDR
Sections:
; MetaM
db "F***", 0, 0, 0, 0 ; null name
dd metamSize ; size
dd MetaM ; addr RVA
dd metamSize ; length
dd MetaM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0C0000040h ; flags: datasection writeable readable
; CodeM
db "Windows", 0 ; null name
dd codemSize ; size
dd CodeM ; addr RVA
dd codemSize ; length
dd CodeM ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
; IMPORTS
db ".idata", 0, 0
dd importsSize ; size
dd IMPORTS ; addr RVA
dd importsSize ; length
dd IMPORTS ; pos
dd 0 ; no relocations
dd 0 ; no linenum
dw 0
dw 0
dd 0E0000020h ; flags: codesection writeable readable executable
fileSize equ $
;END.
P.S。我觉得很奇怪,编程中的一个事实上的标准是使用颜色来突出显示语法,而不是使用颜色来突出显示含义。就此而言,甚至 boldface/italics 也不行。所以,我希望我可以将源代码中的关键部分着色或加粗 - 唉,这是不可能的。在我的编程环境 - BlackBox Component Builder - 我可以自由使用颜色和 bold/italic 我想要的一切: