2
$\begingroup$

I want to extract information from executable files, such as the names and sizes of sections, symbols, etc. The Wikipedia article has a diagram showing the layout of a PE. See also this diagram

It seems Mathematica isn't able to decode portable executables out of the box:

exe = Import["C:\\Windows\\System32\\notepad.exe"];
(* cannot infer the format *)

But we can at least read the bytes:

Import["C:\\Windows\\System32\\notepad.exe", "Byte"]

(* or alternatively *)

BinaryReadList["C:\\Windows\\System32\\notepad.exe"]

... so I've tried loading the bytes in myself and decoding the PE byte-by-byte:

(* extract bytes at zero-indexed offsets *)
at[bytes_, i_] := bytes[[i + 1]]
at[bytes_, i_, j_] := bytes[[i + 1 ;; j + 1]]

(* convert little-endian bytes to uint *)
uint[bytes_] := FromDigits[Reverse@bytes, 16]

(* Create a null terminated string from some bytes *)
nulltermstr[bytes_] := FromCharacterCode[TakeWhile[bytes, # > 0 &]]

exe = OpenRead["C:\\Windows\\System32\\notepad.exe", BinaryFormat -> True];
bytes = BinaryReadList[exe];
Close[exe];

peHeaderOffset = uint@at[bytes, 60, 63];
peMagic = FromCharacterCode[at[bytes, peHeaderOffset, peHeaderOffset + 1]]
(* result: PE *)

optionalHeaderSize = uint[at[bytes, peHeaderOffset + 5*4, peHeaderOffset + 5*4 + 1]];
sectionsStartOffset = peHeaderOffset + 3*8 + optionalHeaderSize;
sectionSize = 5*8;
sectionNames = Table[
  nulltermstr[at[bytes, sectionsStartOffset + sectionSize*i, sectionsStartOffset + sectionSize*i + 7]]
  , {i, 0, 6}]
(* result: {".text",".rdata",".data",".pdata",".didat",".rsrc",".reloc"} *)

It's quite tedious extracting pointers and getting the offsets from the binary format. As you can see, there are 'magic numbers' in my code which is not ideal.

Is there a better, ideally more declarative way to specify the format so that Mathematica can parse these binary headers? Ideally I'd like to be able to declare the structure of the PE file in a way similar to this tool: http://kaitai.io which would make decoding all sorts of binary formats much simpler from within Mathematica.

$\endgroup$
2
  • 2
    $\begingroup$ Import["C:\\Windows\\System32\\notepad.exe", "Byte"] works for me. So does BinaryReadList["C:\\Windows\\System32\\notepad.exe"] $\endgroup$ Commented Jun 25, 2020 at 16:06
  • $\begingroup$ @SjoerdSmit thanks I will remove that part of the question since I was just using it wrongly. $\endgroup$
    – flinty
    Commented Jun 25, 2020 at 17:20

1 Answer 1

3
$\begingroup$

This is not a pure Mathematica solution, but I ended up calling Python and using the pefile module:

{sections, sectionsEntropy, importedSymbols} =
  ExternalEvaluate["Python", {
     "import pefile",
     "pe = pefile.PE('C:/Windows/System32/notepad.exe')", 
     "[x.Name.decode('ascii').rstrip('\\x00') for x in pe.sections]",
     "[x.get_entropy() for x in pe.sections]",
     "[[[x.dll.decode('utf-8'),[s.name.decode('utf-8'), s.address]] for s in x.imports if s.name] for x in pe.DIRECTORY_ENTRY_IMPORT]"
  }][[3 ;;]];

BarChart[sectionsEntropy, ChartLabels -> sections, AxesLabel -> "Entropy"]

(* show the symbols imported from in msvcrt.dll. Here it's the 3rd dll import *)
Grid[Flatten /@ importedSymbols[[3, All, 2 ;;]]]

executable sections entropy and symbols

$\endgroup$

Your Answer

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

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