How do you reliably extract appended data in a Portable Executable?
Just to clarify: the appended data - also called overlay - is the part of a PE file that is not covered by the header.
Because of some tricky conditions in the PE file format, it might be difficult to determine in some extreme case, so it's better to rely on a robust library such as pefile.
Here is a simple Python script that relies on pefile to extract the appended data:
import pefile
import sys
filename = sys.argv[1]
with open(filename, "rb") as s:
r = s.read()
pe = pefile.PE(filename)
offset = pe.get_overlay_data_start_offset()
with open(filename + ".app", "wb") as t:
t.write(r[offset:])
use with your filename as argument.
Personally, I usually do it with Hiew - as Hiew is faster to start than anything else, and also PE-robust:
- go at appended data start
- in
Hex
orASM
mode, press F8 to view PE header information - Alt+F2 to go at appended data start
- in
- select until the end
- Keypad-
*
to start selection - Ctrl+End to go at bottom of the file
- Keypad-
*
again to finish selection
- Keypad-
- F2 to write selection to file
-
2
-
no, the Authenticode signature must be last in the file or it will fail validation. You cannot (any more) append data after the signature. – peter ferrie May 7 '13 at 17:11
-
There could be a signature at the end of the appended data, so one might not want to select until the bottom of the file. – Ange May 7 '13 at 18:40
-
1if there is a signature then it'll be recorded in the data directory, so you have the start offset and size there, its pretty simple – evlncrn8 Oct 11 '14 at 22:50
-
It is not that hard to do manually.
- Find the PE header from the MZ header and determine the location of the section table.
Walk the section table, and determine the maximum value of
PointerToRawData
+SizeOfRawData
. (note: these values are subject to alignment using theFileAlignment
member of theIMAGE_OPTIONAL_HEADER
).Use the determined maximum as the file offset to the overlay data.
Note that some installers/file formats do not actually use this calculation, but they instead have a small trailer at the end of file which points to the beginning of the payload. For example, ZIP file format works like that - that's why a self-extracting ZIP can be extracted irregardless of whether the unpacker stub is PE, DOS MZ, ELF, Mach-O or anything else.
-
no, this is not sufficient. SizeOfRawData can have any value, even gigabytes large in a small file. It's bound by VirtualSize at that point. Accurately determining the end of the image is not trivial. – peter ferrie May 6 '13 at 19:13
-
@peterferrie What would be a good algorithm? I am actually trying to implement one. pefile does it roughly as suggested here. – Karsten Hahn May 13 '14 at 8:12
-
@Veitch, my answer to your other question now has sample code and a description, which should be what you need. – peter ferrie May 13 '14 at 22:35
Another option is the mewrev/pe Go package:
package main
import (
"github.com/mewrev/pe"
"io/ioutil"
"log"
"os"
)
func main() {
file, e := pe.Open("vs_BuildTools.exe")
if e != nil {
log.Fatal(e)
}
y, e := file.Overlay()
if e != nil {
log.Fatal(e)
}
ioutil.WriteFile("vs_BuildTools.overlay", y, os.ModePerm)
}