Windows 10 won't recognize hand-made PE executables that work in WINE

你。 提交于 2021-01-07 02:48:32

问题


I have made (in assembler, without a linker) an EXE for x86-64 that runs perfectly well in Wine under Linux. It's a basic HelloWorld that calls MessageBoxA and ExitProcess.

Windows 10 won't recognize it, saying 'This program cannot be executed on your computer, talk to your vendor for a version that will suit your computer'.

I have used PE format readers (PE Tools and CFF Explorer) to analyze my PE EXE. All numbers in the PE Optional header are the same as in other working EXEs (like os versions, subsystem versions). Only the ones that are specific to my file and sections are different. And Windows won't recognize the file as executable on my computer.

Where do I even begin to look beyond the WIndows error message? Are there any tools that allow to check EXE validity with a more specific error messaging than 'Bad exe'? (this is what xdbg reports.

On Wine, I was able to do WINEDEBUG=+all wine my.exe and that gave me hints into what was wrong, and I was able to fix it and get it to work. Any such tools in 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:

回答1:


There are a number of issues here. The fact that you say this code runs on wine suggests that wine is very forgiving. Windows? Not so much.

To start with, here's the build command I'm using (based on OP's original code above): nasm.exe org.asm -o org.exe

Using dumpbin (from VS2019) against org.exe gives us:

File Type: EXECUTABLE IMAGE
org.exe : fatal error LNK1107: invalid or corrupt file: cannot read at 0x31DE

Not a promising beginning. The first thing I did (that made a difference) was to change this code down in Sections:

; MetaM
db  "F", 0, 0, 0, 0 ; null name

...

; CodeM
db  "W", 0  ; null name

By spec, these are supposed to be 8 bytes long, not just null terminated strings. Changed these and now dumpbin gives me:

File Type: EXECUTABLE IMAGE

  Summary

        1000 .idata
        1000 CodeM
        1000 MetaM

Better. My next step was dumpbin /headers a.exe, which gave me:

LINK : fatal error LNK1000: Internal error during ReadOptionalHeader

This was fixed by uncommenting the line under directories: times 14 dq 0.

I won't post the entire dumpbin output, but suffice to say that it now shows the headers for all the sections.

Next thing was to look at dumpbin /imports a.exe. Instead of grouping all the imports for each dll, every import was given its own section here. That can't be right. So I fixed the iData section. There was a problem with alignment that I fixed too. There's probably a dozen other things that need fixing, but at least now it runs:

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:

Now that it's working, I'll leave the cleanup to you. Tell Ann I said hi...


Update:

So I did some cleanup.

  • More documentation
  • Fewer hard-coded values (calc'ed where possible)
  • Uses 512 byte alignment in file (smaller image size) while still using 4k pages (to allow page protection). As a result, different fields use different offsets.

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



回答2:


I want to post the eventual version that works both in Wine and Windows 10. Briefly, here's what was wrong:

  1. Misaligned image size field. Has to be page-aligned (1000h). My mistake.
  2. Miscalculated headers size. Poor specification. This helped me figure it out, not the spec: PE format walkthru
  3. Having 2 directories doesn't work, thought it doesn't contradict the specification. I didn't experiment with numbers other than 16. Poor specification. By the way, IAT in the directories is irrelevant; indeed, if you import 2 DLLs, each gets and IAT; which one should be referenced in the directories? Answer: the loader couldn't care less.
  4. An IAT's first entry has to be non-zero, otherwise this IAT is ignored and not filled out. This is outrageousely undocumented. Poor specification.

On a positive note,

  1. I was able to place PE header and sections list at the end of the file, unlike conventionally placing it after the DOS stub and before the sections.
  2. I was able to organize the import in a somewhat weird way with one IAT per each imported symbol rather than one IAT per DLL. In both these issues, both I and the loader follow the letter of the specification, which is a good thing.
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. I find it so weird that a defacto standard in programming is to use color for syntax highlighting, and not use color for meaning highlighting. Not even boldface/italics for that matter. So, I wish I could just colorize or boldface the critical pieces in the source - alas, not possible. In my programming environment - the BlackBox Component Builder - I am free to use color and bold/italic all I want:



来源:https://stackoverflow.com/questions/65405546/windows-10-wont-recognize-hand-made-pe-executables-that-work-in-wine

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!