【发布时间】:2020-12-22 08:41:52
【问题描述】:
我已经(在汇编程序中,没有链接器)制作了一个适用于 x86-64 的 EXE,它在 Linux 下的 Wine 中运行得非常好。这是一个调用 MessageBoxA 和 ExitProcess 的基本 HelloWorld。
Windows 10 无法识别它,提示“此程序无法在您的计算机上执行,请与您的供应商联系以获取适合您计算机的版本”。
我使用 PE 格式阅读器(PE 工具和 CFF Explorer)来分析我的 PE EXE。 PE Optional 标头中的所有数字都与其他工作 EXE(如操作系统版本、子系统版本)中的相同。只有特定于我的文件和部分的那些是不同的。而且 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:
【问题讨论】:
-
我不知道这是否适用,但last 时我看到 Windows 无法加载 exe,它需要一个 RDATA 部分(即使它没有使用)。跨度>
-
嗯.. 我只是能够制作另一个没有重定位的 .exe(使用 nasm + golink)=> 没有 RDATA,并且在 Windows 10 和 Wine 下都运行得很好。我也重新拥有 16 个目录,而不仅仅是我需要的 2 个。无济于事。
-
您在 Optional 标头中仅声明了两个数据目录,但 PE 通常有 16 个(尽管其中大多数是零)。我怀疑 Windows 加载程序在尝试将 MessageBoxA 和 ExitProcess 的 VA 绑定到 ImportAddress 表时失败,并且它发现 IAT 根本不存在于您的 PE 中..
-
在上一条评论中,我提到我已经回到了其中的 16 个目录中,但这并没有帮助。我还将 PE 标头移回文件中,并将其放在 DOS 存根之后 - 就像大多数 exe 一样,并重新排列导入部分以仅引用每个模块一次。现在 Windows 不会给出该消息。相反,应用程序会静默失败。在 x64dbg 中,我发现 Windows 加载了我的 exe,但没有链接它。一直以来,Wine 都执行了我尝试过的每一个符号 PE 布局,并成功链接并运行了我的 exe。
标签: assembly windows-10 portable-executable wine