Code Crunching – Tiny PE (challenge) [update #2: now with 304 bytes!]

[ Update: tiny PE is now at 384 bytes. The follow-up post and one of the techniques used to achieve this can be found here.
Update #2: tiny PE is now at 304 bytes: ]

Or shall I dare saying the Tiniest?

It all began a few days ago, a few friends challenged me to write a smaller PE executable than theirs.

Ground rules

It might sound fairly easy at first, but it’s not, and there is merely one simple goal:
Grab a file from the Internet and execute it.

With the following rules:
1) Only Imports section is allowed for kernel32.dll to get LoadLibraryA and GetProcAddress.
2) All strings must not be viewable (except rule 1), so we xor… :)

After working dozens of hours I came up with something extremely crazy, 411 bytes.

Most of the debuggers will hate this file and it has anti-disassemblers effect. I couldn’t check it on many systems, just my WinXP and it works nicely.

Here’s the hex:

Hex of Tiny PE

Tricks used

If any of you have ever tried to do this before, the common mistake is that the minimum .exe file size you can get is 0×200 bytes. Since the File Alignment has to be at least 0×200. The OS Loader gracfully ignores it, luckily :)

The code itself is really efficient, though it may still be possible to shave a few more bytes off. In my opinion that extra work on it is not worth it under the 80/20 rule.

To achieve this, lots of tricks were needed. To name a few, I used:
1) All (well almost) sizes fields of DataDirectory (to place my code).
2) Unused fields in the Section structure (to store a string).
3) Unused fields in the Import Module Descriptor (to store another string).
4) The fact that the OS Loader will zero the caves (from end of my code to segment alignment).

And many other things! I even tried to change the size of the optional header, though one might say it was a pointless effort…

What I did

I went through three main stages while working on this:
First was to crunch the PE itself, proceed to write some code and then seriously mix these two methods.

I had to see how the OS Loader works, because before all this work, it didn’t want to load my file. Thanks to WinDbg it was easier to do so. The funny thing is that most of the file (35%) are zero bytes, the rest are the strings and then the code itself which is sparsely located all over the file.

The file can be found here:

Hint: xor the file with 0xAA to see its resources.

Tools I used for this research: my very own diStorm64, WinDbg, OllyDbg, IDA Pro, Yasm, RTA, Python, LordPE, PE Tools and as you can see Hexplorer.

It almost looks like I was in the middle of a war between all these tools :)
BTW – The file will download a stub (showing a message box) from my site and launch it.

An older very crazy code crunching challenge I did was this:


What do you think, how can we make it smaller?
I am going to work on this some more later in the week. My target is reaching less than 400 bytes, but maybe someone else will get there first?

Lame tricks such as using a shorter URL or msiexec should not be used.


Gil Dabah

  • z00le’s results:

    Antivirus – Result
    AntiVir – no virus found
    Authentium – could be a corrupted executable file
    Avast – no virus found
    AVG – no virus found
    BitDefender – no virus found
    CAT-QuickHeal – (Suspicious) – DNAScan
    ClamAV – no virus found
    DrWeb – no virus found
    eTrust-InoculateIT – no virus found
    eTrust-Vet – no virus found
    Ewido – no virus found
    Fortinet – suspicious
    F-Prot – no virus found
    F-Prot4 – no virus found
    Ikarus – no virus found
    Kaspersky – no virus found
    McAfee – no virus found
    Microsoft – no virus found
    NOD32v2 – probably unknown NewHeur_PE virus
    Norman – W32/Downloader
    Panda – Suspicious file
    Sophos – no virus found
    TheHacker – no virus found
    UNA – no virus found
    VBA32 – suspected of Win32.Trojan.Downloader (http://…)
    VirusBuster – no virus found

    File size: 411 bytes
    MD5: 53318a6d5850525559abf7a50f711a35
    SHA1: a58001654822f05d45081df3297a891b9a5459ea
    norman sandbox: [ General information ]
    * File length: 411 bytes.

    [ Changes to filesystem ]
    * Creates file .exe.

    [ Network services ]
    * Downloads file from http://ragestorm .net/f.exe as /f.exe.

    [ Security issues ]
    * Starting downloaded file – potential security problem.

  • Matthew Murphy

    If your target is Windows XP with the WebClient service enabled (default), the download-and-execute process is one API call:

    WinExec("\\\f.exe", SW_SHOW)

    WinExec() attempts to create a process using this file, which results in a file open/file mapping request. The first time the file is hit, a page fault occurs, and the kernel initiates the download of the file content. This eventually reaches MRXDAV.SYS, which will pull down the file using WebDAV over HTTP. Game over in one API call — if the WebDAV redirector does the job right.  For it to work, you’d have to use mod_dav or some similar solution, and make sure your root directory is accessible (for reads, not writes) to DAV clients.

    You can avoid importing anything this way.  If you load a Windows subsystem binary with no imports what-so-ever, the second library loaded is kernel32.dll.  Using the PebLdrData structure at FS:[30h], you can find the base address of kernel32.dll.  You could then locate your target import using a hash, or a direct string compare if that turns out to be smaller.

  • arkon

    Hey Matthew,
    nice way, didn’t know WinExec could do that (indirectly however).

    Anyways, the target, though not specified, is that the PE file will run on all systems.

    The problem with the way you suggested is that I will have to write a kind of my own GetProcAddress which is larger than the WHOLE code I wrote. The code isn’t the biggy, it’s the strings. :)

  • Evil Dave

    On a somewhat related note, here is a description of how to produce a tiny Linux executable.

  • 0×90

    >You can avoid importing anything this way. If you
    >load a Windows subsystem binary with no imports
    >what-so-ever, the second library loaded is
    >kernel32.dll. Using the PebLdrData structure at
    >FS:[30h], you can find the base address of
    >kernel32.dll. You could then

    Wrong. The file won’t run on Windows 2000 if you don’t import at least kernel32.. and it depends of the SP too.. that’s why packers still import at least a few functions.

  • Gaius

    Wrong too, XP/2k share almsot identical loaders, when importless module is loaded, kernel is already in memory, along with ntdll and such. I’ve created xp/2k exe that downloads this f.exe file, data is encrypted (code too!), no import table and size is equ to 298 bytes.

  • Gaius

    Ive rechecked my code and I think i can get it down to 250, but it would work only on XP then. (yes it also executes the file)

  • Arkon

    Email it to me please.
    FYI if you remove the RVA and Sizes and change optional header size then your .exe is not compatible with all Windows versions.

  • 0×90

    >Wrong too, XP/2k share almsot identical loaders,
    >when importless module is loaded, kernel is already
    >in memory, along with ntdll and such. I’ve created

    Even though what you say is right, earlier versions of 2K wouldn’t run the file without at least one kernel32 import.

  • sunshine

    The Code-Crunchers mailing list has just been established:

  • sunshine
  • find the valur of r-:(160)in octal=(304)r

    give me answer right now.

  • hoya

    i appreciate your study.

    i want to know what is your hex editor.