PE Inspector: Windows EXE and DLL Viewer
Reads a Windows PE image the way dumpbin or pefile does, straight out of the bytes and without uploading anything. The MS-DOS header at the front is a fossil: the only field that still matters is e_lfanew at offset 0x3C, which is where the real "PE\0\0" signature and the COFF header begin. From there you get the machine (x86, x86-64, ARM64, ARM64EC, Itanium, RISC-V and the rest), whether the optional header is PE32 or PE32+, whether the file header carries the DLL characteristic, and which subsystem the loader will start it under — Windows console, Windows GUI, native, or one of the EFI flavours. The hardening row is the Windows counterpart of checksec, read from DllCharacteristics: ASLR (DYNAMIC_BASE), high-entropy ASLR, which is what raises a 64-bit image from eight bits of relocation randomness to seventeen or more, DEP/NX (NX_COMPAT), Control Flow Guard (GUARD_CF), whether structured exception handling was suppressed (NO_SEH), forced integrity checking, and whether a certificate table exists at all. Sections are listed with virtual address, virtual and raw size and decoded characteristics; one that is both writable and executable is flagged, because outside a packer, an installer or self-modifying code there is almost never a reason for it. Imports are grouped by DLL with each function named, or shown as #ordinal where the file imported by ordinal and so never recorded a name — that table is the honest answer to "what does this binary actually need", and it is the first thing an analyst reads. Exports give the name the library publishes itself under, the ordinal base (very often not 1) and every exported name. Two limits stated plainly. A non-empty certificate directory means the file carries a signature, not that the signature is valid, current, or from anyone you trust; verifying Authenticode needs the certificate chain and a timestamp authority and is out of scope here. And the link timestamp is just a field a linker writes, so a reproducible build will have set it to a constant or a hash and it proves nothing about when the file was made. Malformed input is refused rather than half-read: a bad MZ magic, a DOS header that does not point at a PE signature, an unknown optional-header magic, and a section that claims more raw data than the file contains each produce a named error.
How to use
- Drop the file on the box. An .exe, .dll, .sys driver, .ocx control or .efi binary all parse the same way — the extension is not what decides.
- Read the header tiles for the architecture, PE32 versus PE32+, EXE versus DLL and the subsystem. A console and a GUI build of the same program differ only here.
- Check the hardening chips. ASLR, DEP and CFG are what a current Windows toolchain sets by default, so an amber chip on a recent binary is worth explaining before you ship it.
- Scan the imports for the capability you care about: WS2_32 means sockets, WININET or WINHTTP means it talks to the web, ADVAPI32 means registry or service work, CRYPT32 means certificates.
- Look at the section table for anything writable and executable, and at the data directories for a certificate, a debug entry or a CLR runtime header that changes how the rest should be read.
Frequently asked questions
- The file has a signature chip. Does that mean it is safe?
- No, and the distinction matters. The chip only says the certificate data directory is non-empty, which means an Authenticode blob is appended after the last section of the file. It does not say that the blob matches the bytes in front of it, that the signing certificate chains to a root Windows trusts, that the certificate was valid at signing time, or that the signer is who the file claims. Any of those can be false while the directory is still populated — a file can be re-signed with a self-issued certificate, or the payload can be modified after signing so the hash no longer matches. Verifying it properly needs the full chain, revocation data and usually a trusted countersignature timestamp, which is a network operation and deliberately out of scope for a browser tool that promises never to upload your file.
- What does high-entropy ASLR add over plain ASLR?
- Plain ASLR moves an image to a random base at load time, but on 32-bit Windows only about eight bits of the address are actually random, which is few enough to brute-force in a loop. HIGH_ENTROPY_VA tells the loader that the image is safe to place anywhere in the full 64-bit address space, which raises the randomised bits into the high teens and makes guessing impractical. It only means anything on a PE32+ image that also has DYNAMIC_BASE set, and it further requires that the program never truncates a pointer to 32 bits — which is why linkers gate it behind /HIGHENTROPYVA and why a 64-bit binary built from careless 32-bit-era source may deliberately leave it off.
- Why do some imported functions show as #12 instead of a name?
- Because the file imported them by ordinal. Each entry in a thunk array is a machine word whose top bit is a flag: when it is set, the low 16 bits are the ordinal to look up in the exporting DLL's address table, and no name is stored anywhere in the importing file. When it is clear, the rest of the word is an RVA pointing at a hint/name pair and the name is right there. Importing by ordinal is slightly faster to resolve and was common in older code; it is also what you see when someone wants to obscure which API is being called. This tool shows the raw ordinal rather than guessing, because the mapping from ordinal to name lives in the exporting DLL and depends on which version of it is installed.
- The section table shows a section that is writable and executable. How bad is that?
- It is unusual enough to be worth a look, but it is not proof of anything on its own. A normal compiler and linker emit .text as read plus execute and .data as read plus write, and never both at once, because DEP exists precisely to make that combination fault. Sections carrying IMAGE_SCN_MEM_WRITE and IMAGE_SCN_MEM_EXECUTE together usually come from a runtime packer that decompresses code into its own section before jumping into it — UPX names its section UPX1 — or from an old installer, a self-modifying protection scheme, or a JIT that allocates in the image rather than at runtime. If you did not expect any of those, the binary is doing something worth understanding before you run it.
- The link timestamp says 1970, or a date in the future. Is the file corrupt?
- Almost certainly not. TimeDateStamp is a 32-bit seconds-since-1970 field that the linker fills in, and modern toolchains stopped treating it as a date. A reproducible build sets it to zero or to a fixed constant so that the same source always yields byte-identical output, and MSVC with /Brepro writes a hash of the inputs there instead, which lands at an arbitrary and often far-future point on the calendar. Rich header data and the debug directory are usually better evidence of the toolchain than this field is, and none of it is evidence of when a file was actually shipped. Treat it as metadata that happens to be shaped like a date.
- This is a .NET assembly. Why do the imports and the entry point look almost empty?
- Because for a managed assembly the PE part is a shell. When the CLR runtime data directory (index 14, the COM descriptor) is non-empty, the real program is IL stored inside .NET metadata, and the native headers exist mainly to satisfy the Windows loader. A classic C# executable has exactly one import, _CorExeMain from mscoree.dll, an entry point that is a two-instruction jump to it, and a .text section that is mostly metadata rather than machine code. So the architecture field may say Intel 386 for a program that will happily run 64-bit, and the section sizes tell you nothing about how big the program is. Reading it properly needs an IL disassembler such as ildasm or ILSpy.
Related tools
Mach-O Binary Inspector
Open a macOS or iOS binary in your browser: the architectures inside a universal file, segments and sections, linked dylibs, rpaths, UUID and signing.
ELF Binary Inspector
Open a Linux executable, .so or .o in your browser: architecture, shared libraries it needs, build ID, and whether it is PIE, NX and RELRO hardened.
Java Class File Inspector
Read a compiled .class file in the browser: class file version and JDK release, access flags, constant pool, fields, methods and referenced classes.
gzip (.gz) Inspector
Read a .gz header field by field, walk every member, and verify the trailer by recomputing CRC32 and the real size in your browser.
xz (.xz) Stream Inspector
Read an .xz container: stream header, every block header and filter chain, the index and the footer — with the true uncompressed size, unpacking nothing.
Text Encoding Converter
Open text files in legacy encodings (EUC-KR, Shift_JIS, Windows-1252…) as readable UTF-8.