Nytro Posted September 30, 2014 Report Posted September 30, 2014 PE Trick #1: A Codeless PE Binary File That Runs Introduction One of the annoying things of my Windows Internals/Security research is when every single component and mechanism I’ve looked at in the last six months has ultimately resulted in me finding very interesting design bugs, which I must now wait on Microsoft to fix before being able to talk further about them. As such, I have to take a smaller break from kernel-specific research (although I hope to lift the veil over at least one issue at the No Such Conference in Paris this year). And so, in the next following few blog posts, probably inspired by having spent too much time talking with my friend Ange Albertini, I’ll be going over some neat PE tricks. Challenge Write a portable executable (PE/EXE) file which can be spawned through a standard CreateProcess call and will result in STATUS_SUCCESS being returned as well as a valid Process Handle, but will notContain any actual x86/x64 assembly code section (i.e.: the whole PE should be read-only, no +X section) Run a single instruction of what could be construed as x86 assembly code, which is part of the file itself (i.e.: random R/O data should not somehow be forced into being executed as machine code) Crash or make any sort of interactive/visible notice to the user, event log entry, or other error condition. Interesting, this was actually a real-world situation that I was asked to provide a solution for — not a mere mental exercise. The idea was being able to prove, in the court of law, that no “foreign” machine code had executed as a result of this executable file having been launched (i.e.: obviously the kernel ran some code, and the loader ran too, but all this is pre-existing Microsoft OS code). Yet, the PE file had to not only be valid, but to also return a valid process handle to the caller. SolutionHEADER:00000000 ; IMAGE_DOS_HEADERHEADER:00000000HEADER:00000000 .686pHEADER:00000000 .mmxHEADER:00000000 .model flatHEADER:00000000HEADER:00000000 ; Segment type: Pure dataHEADER:00000000 HEADER segment page public 'DATA' use32HEADER:00000000 assume cs:HEADERHEADER:00000000 __ImageBase dw 5A4Dh ; PE magic numberHEADER:00000002 dw 0 ; Bytes on last page of fileHEADER:00000004 ; IMAGE_NT_HEADERSHEADER:00000004 dd 4550h ; SignatureHEADER:00000008 ; IMAGE_FILE_HEADERHEADER:00000008 dw 14Ch ; MachineHEADER:0000000A dw 0 ; Number of sectionsHEADER:0000000C dd 0 ; Time stampHEADER:00000010 dd 0 ; Pointer to symbol tableHEADER:00000014 dd 0 ; Number of symbolsHEADER:00000018 dw 0 ; Size of optional headerHEADER:0000001A dw 2 ; CharacteristicsHEADER:0000001C ; IMAGE_OPTIONAL_HEADERHEADER:0000001C dw 10Bh ; Magic numberHEADER:0000001E db 0 ; Major linker versionHEADER:0000001F db 0 ; Minor linker versionHEADER:00000020 dd 0 ; Size of codeHEADER:00000024 dd 0 ; Size of initialized dataHEADER:00000028 dd 0 ; Size of uninitialized dataHEADER:0000002C dd 7FBE02F8h ; Address of entry pointHEADER:00000030 dd 0 ; Base of codeHEADER:00000034 dd 0 ; Base of dataHEADER:00000038 dd 400000h ; Image baseHEADER:0000003C dd 4 ; Section alignmentHEADER:00000040 dd 4 ; File alignmentHEADER:00000044 dw 0 ; Major operating system versionHEADER:00000046 dw 0 ; Minor operating system versionHEADER:00000048 dw 0 ; Major image versionHEADER:0000004A dw 0 ; Minor image versionHEADER:0000004C dw 4 ; Major subsystem versionHEADER:0000004E dw 0 ; Minor subsystem versionHEADER:00000050 dd 0 ; Reserved 1HEADER:00000054 dd 40h ; Size of imageHEADER:00000058 dd 0 ; Size of headersHEADER:0000005C dd 0 ; ChecksumHEADER:00000060 dw 2 ; SubsystemHEADER:00000062 dw 0 ; Dll characteristicsHEADER:00000064 dd 0 ; Size of stack reserveHEADER:00000068 dd 0 ; Size of stack commitHEADER:0000006C dd 0 ; Size of heap reserveHEADER:00000070 dd 0 ; Size of heap commitHEADER:00000074 dd 0 ; Loader flagHEADER:00000078 dd 0 ; Number of data directoriesHEADER:0000007C HEADER endsHEADER:0000007C endAs per Corkami, in Windows 7 and higher, you’ll want to make sure that the PE is at least 252 bytes on x86, or 268 bytes on x64. Here’s a 64 byte Base64 representation of a .gz file containing the 64-bit compatible (268 byte) executable: H4sICPwJKlQCAHguZXhlAPONYmAIcGVg8GFkQANMDNxoYj+Y9tUjeA4MLECSBc5HsB1QTBk6AABe6Mo9DAEAAA==Caveat There is one non-standard machine configuration in which this code will actually still crash (but still return STATUS_SUCCESS in CreateProcess, however). This is left as an exercise to the reader. Conclusion The application executes and exits successfully. But as you can see, no code is present in the binary. How does it work? Do you have any other solutions which satisfy the challenge?Sursa: PE Trick #1: A Codeless PE Binary File That Runs « Alex Ionescu’s Blog Quote