4

If I pack an executable using UPX and then unpack the executable using UPX -d, the executables are not the same. I understand techniques for unpacking executables that are packed using UPX, but I was wondering - what happens to the executable during packing/unpacking? How does this affect the preservation of information inside the executable? (For example, if I compile C code and then decompile it I'll have lost a lot of variable name information.)

EDIT: I've verified that after UPX packing/repacking the binary is not the same. The state machine looks something like:

#1 (Original) ----UPX Pack----> #2 (Packed)

#2 (Packed) -----UPX Unpack----> #3 (Unpacked)

#3 (Unpacked) -----UPX Pack-----> #4 (Repacked)

#4 (Repacked) -----UPX Unpack----> #3 (Unpacked)

Where #1, #2, #3, and #4 all have different hashes.

5
  • I may not understand your question, but AFAIK if we unpack with upx -d then we receive the same binary as before packed (since this procedure does not depend on the unpacking stub embedded in the packed binary)? Otherwise, if we let the stub unpack the binary, then the received binary is different (either we need fix IAT and OEP, or the stub is not standard, then it can modify the packed data) Mar 18, 2016 at 1:43
  • See my edit to the original question. I was going to clarify this in a comment, but all the formatting was stripped out. Before making any assumptions about what the packer does, do a controlled test and take a hash of the binary at each step. Mar 18, 2016 at 14:09
  • Aha, you compare the hash values :) Mar 19, 2016 at 3:16
  • Have you considered using a hexdump to visualize (to yourself and to us) the differences between those four stages? Because there's a lot that can be done to yield what's more or less semantically (within certain limits) the same binary, but with that binary being different from another. Two files built at different times will also yield a different binary (and hence hash) depending on the linker used.
    – 0xC0000022L
    Apr 4, 2016 at 12:29
  • Yeah, I used vbindiff to confirm that there were differences in the binaries. I've also used CFFExplorer to see some of the differences, like reordering of the imports. The files have more than just different timestamps - there are different sections in the executable and the imports are ordered differently. Apr 4, 2016 at 19:09

1 Answer 1

9

If you compare the hash values of original/unpacked files, then they are different since upx -d does not restore bit-by-bit of the original file. Indeed, UPX parses the original file and keeps only information so that the packed data, after being unpacked, can be executed exactly the same as the original one, i.e. the original/unpacked files are semantically equivalent but not physically equivalent^^.

That is understandable since there is information that not affect to the execution of the binary, a trivial instance is the data between the end of DOS stub and the begin of PE header. For more detail, you may refer to the function pack of the class, for example, PackW32Pe (in p_w32pe.h/cpp) for PE and the function unpack of the class Packer (in packer.h/cpp).

For example, we can see that UPX modifies the (DOS and PE) header of the unpacked file using the following code (I have renamed some variables for more comprehensible). First, it decompresses and extracts the header:

// decompress
decompress(input_buffer, output_buffer);
upx_byte *extrainfo = output_buffer + get_le32(output_buffer + ph.u_len - 4);

memcpy(&output_header, extrainfo, sizeof (output_header));

then modifies slightly the header:

...
output_header.headersize = rvamin;
output_header.chksum = 0;

//NEW: disable reloc stripping if ASLR is enabled
if(input_header.dllflags & IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE)
    opt->win32_pe.strip_relocs = false;

// FIXME: ih.flags is checked here because of a bug in UPX 0.92
if ((opt->win32_pe.strip_relocs && !isdll) || (input_header.flags & RELOCS_STRIPPED))
{
  output_header.flags |= RELOCS_STRIPPED;
  ODADDR(PEDIR_RELOC) = 0;
  ODSIZE(PEDIR_RELOC) = 0;
}

// write decompressed file
if (output_file)
{
...
2
  • 1
    But why does packing and unpacking it twice still result in different files? What is kept during the first/pack unpack round, that is removed the second time around?
    – Viktor
    Mar 21, 2016 at 18:51
  • 2
    @Viktor, after packing and unpacking the first time, things such as the import table are rebuilt rather than restored. This change causes the next packing to pack the file a bit differently, resulting in the table being rebuilt differently after unpacking. Mar 23, 2016 at 20:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.