230 lines
9.1 KiB
C
230 lines
9.1 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||
│ │
|
||
│ This program is free software; you can redistribute it and/or modify │
|
||
│ it under the terms of the GNU General Public License as published by │
|
||
│ the Free Software Foundation; version 2 of the License. │
|
||
│ │
|
||
│ This program is distributed in the hope that it will be useful, but │
|
||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||
│ General Public License for more details. │
|
||
│ │
|
||
│ You should have received a copy of the GNU General Public License │
|
||
│ along with this program; if not, write to the Free Software │
|
||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||
│ 02110-1301 USA │
|
||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||
#include "libc/bits/safemacros.internal.h"
|
||
#include "libc/calls/calls.h"
|
||
#include "libc/calls/struct/stat.h"
|
||
#include "libc/conv/conv.h"
|
||
#include "libc/nt/struct/imagedosheader.internal.h"
|
||
#include "libc/nt/struct/imagentheaders.internal.h"
|
||
#include "libc/nt/struct/imageoptionalheader.internal.h"
|
||
#include "libc/stdio/stdio.h"
|
||
#include "libc/sysv/consts/map.h"
|
||
#include "libc/sysv/consts/o.h"
|
||
#include "libc/sysv/consts/prot.h"
|
||
#include "third_party/xed/x86.h"
|
||
#include "tool/decode/lib/asmcodegen.h"
|
||
#include "tool/decode/lib/flagger.h"
|
||
#include "tool/decode/lib/peidnames.h"
|
||
#include "tool/decode/lib/titlegen.h"
|
||
|
||
/**
|
||
* @fileoverview Portable executable metadata disassembler.
|
||
* @see https://www.aldeid.com/wiki/PE-Portable-executable
|
||
*/
|
||
|
||
static const char *path;
|
||
static struct NtImageDosHeader *mz;
|
||
static size_t mzsize;
|
||
|
||
static struct XedDecodedInst *ildreal(void *addr) {
|
||
static struct XedDecodedInst xedd;
|
||
if (xed_instruction_length_decode(
|
||
xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_REAL), addr,
|
||
XED_MAX_INSTRUCTION_BYTES) != XED_ERROR_NONE ||
|
||
!xedd.length) {
|
||
xedd.length = 1;
|
||
}
|
||
return &xedd;
|
||
}
|
||
|
||
static void startfile(void) {
|
||
showtitle("αcτµαlly pδrταblε εxεcµταblε", "tool/decode/pe", NULL, NULL,
|
||
&kModelineAsm);
|
||
printf("#include \"libc/nt/pedef.internal.h\"\n\n", path);
|
||
}
|
||
|
||
static void *pecheckaddress(struct NtImageDosHeader *mz, size_t mzsize,
|
||
void *addr, uint32_t addrsize) {
|
||
#if !(TRUSTWORTHY + PE_TRUSTWORTHY + 0)
|
||
if ((intptr_t)addr < (intptr_t)mz ||
|
||
(intptr_t)addr + addrsize > (intptr_t)mz + mzsize) {
|
||
abort();
|
||
}
|
||
#endif
|
||
return addr;
|
||
}
|
||
|
||
static void *pecomputerva(struct NtImageDosHeader *mz, size_t mzsize,
|
||
uint32_t reladdr, uint32_t addrsize) {
|
||
return pecheckaddress(mz, mzsize, (void *)((intptr_t)mz + reladdr), addrsize);
|
||
}
|
||
|
||
static void showmzheader(void) {
|
||
showtitle(basename(path), "dos", "mz header",
|
||
"\tMZ = Mark 'Zibo' Joseph Zbikowski\n"
|
||
"\te_cblp: bytes on last page\n"
|
||
"\te_cp: 512-byte pages in file\n"
|
||
"\te_crlc: reloc table entry count\n"
|
||
"\te_cparhdr: data segment file offset / 16\n"
|
||
"\te_{min,max}alloc: lowers upper bound load / 16\n"
|
||
"\te_ss: lower bound on stack segment\n"
|
||
"\te_sp: initialize stack pointer\n"
|
||
"\te_csum: ∑bₙ checksum don't bother\n"
|
||
"\te_ip: initial ip value\n"
|
||
"\te_cs: increases cs load lower bound\n"
|
||
"\te_lfarlc: reloc table offset\n"
|
||
"\te_ovno: overlay number\n"
|
||
"\te_lfanew: portable executable header rva",
|
||
NULL);
|
||
printf("\n");
|
||
show(".ascii", format(b1, "%`'.*s", 2, (const char *)&mz->e_magic),
|
||
"mz->e_magic");
|
||
showshorthex(mz->e_cblp);
|
||
showshorthex(mz->e_cp);
|
||
showshorthex(mz->e_crlc);
|
||
showshorthex(mz->e_cparhdr);
|
||
showshorthex(mz->e_minalloc);
|
||
showshorthex(mz->e_maxalloc);
|
||
showshorthex(mz->e_ss);
|
||
showshorthex(mz->e_sp);
|
||
showshorthex(mz->e_csum);
|
||
showshorthex(mz->e_ip);
|
||
showshorthex(mz->e_cs);
|
||
showshorthex(mz->e_lfarlc);
|
||
showshorthex(mz->e_ovno);
|
||
show(".short",
|
||
format(b1, "%hn,%hn,%hn,%hn", mz->e_res[0], mz->e_res[1], mz->e_res[2],
|
||
mz->e_res[3]),
|
||
"mz->e_res");
|
||
showshorthex(mz->e_oemid);
|
||
showshorthex(mz->e_oeminfo);
|
||
show(".short",
|
||
format(b1, "%hn,%hn,%hn,%hn,%hn,%hn,%hn,%hn,%hn,%hn", mz->e_res2[0],
|
||
mz->e_res2[1], mz->e_res2[2], mz->e_res2[3], mz->e_res2[4],
|
||
mz->e_res2[5], mz->e_res2[6], mz->e_res2[7], mz->e_res2[8],
|
||
mz->e_res2[9]),
|
||
"mz->e_res2");
|
||
showinthex(mz->e_lfanew);
|
||
printf("\n");
|
||
}
|
||
|
||
static void showdosstub(void) {
|
||
}
|
||
|
||
static void showpeoptionalheader(struct NtImageOptionalHeader *opt) {
|
||
showtitle(basename(path), "windows", "pe \"optional\" header", NULL, NULL);
|
||
printf("\n");
|
||
show(".short",
|
||
firstnonnull(findnamebyid(kNtPeOptionalHeaderMagicNames, opt->Magic),
|
||
format(b1, "%#hx", opt->Magic)),
|
||
"opt->Magic");
|
||
showint(opt->MajorLinkerVersion);
|
||
showint(opt->MinorLinkerVersion);
|
||
showinthex(opt->SizeOfCode);
|
||
showinthex(opt->SizeOfInitializedData);
|
||
showinthex(opt->SizeOfUninitializedData);
|
||
showinthex(opt->AddressOfEntryPoint);
|
||
showinthex(opt->BaseOfCode);
|
||
showint64hex(opt->ImageBase);
|
||
showinthex(opt->SectionAlignment);
|
||
showinthex(opt->FileAlignment);
|
||
showshort(opt->MajorOperatingSystemVersion);
|
||
showshort(opt->MinorOperatingSystemVersion);
|
||
showshort(opt->MajorImageVersion);
|
||
showshort(opt->MinorImageVersion);
|
||
showshort(opt->MajorSubsystemVersion);
|
||
showshort(opt->MinorSubsystemVersion);
|
||
showint(opt->Win32VersionValue);
|
||
showinthex(opt->SizeOfImage);
|
||
showinthex(opt->SizeOfHeaders);
|
||
showinthex(opt->CheckSum);
|
||
show(".short",
|
||
firstnonnull(findnamebyid(kNtImageSubsystemNames, opt->Subsystem),
|
||
format(b1, "%#hx", opt->Subsystem)),
|
||
"opt->Subsystem");
|
||
show(".short",
|
||
firstnonnull(RecreateFlags(kNtImageDllcharacteristicNames,
|
||
opt->DllCharacteristics),
|
||
format(b1, "%#hx", opt->DllCharacteristics)),
|
||
"opt->DllCharacteristics");
|
||
showint64hex(opt->SizeOfStackReserve);
|
||
showint64hex(opt->SizeOfStackCommit);
|
||
showint64hex(opt->SizeOfHeapReserve);
|
||
showint64hex(opt->SizeOfHeapCommit);
|
||
showinthex(opt->LoaderFlags);
|
||
showinthex(opt->NumberOfRvaAndSizes);
|
||
}
|
||
|
||
static void showpeheader(struct NtImageNtHeaders *pe) {
|
||
showtitle(basename(path), "windows", "pe header", NULL, NULL);
|
||
printf("\n");
|
||
showorg(mz->e_lfanew);
|
||
show(".ascii", format(b1, "%`'.*s", 4, (const char *)&pe->Signature),
|
||
"pe->Signature");
|
||
show(".short",
|
||
firstnonnull(
|
||
findnamebyid(kNtImageFileMachineNames, pe->FileHeader.Machine),
|
||
format(b1, "%#hx", pe->FileHeader.Machine)),
|
||
"pe->FileHeader.Machine");
|
||
showshort(pe->FileHeader.NumberOfSections);
|
||
showinthex(pe->FileHeader.TimeDateStamp);
|
||
showinthex(pe->FileHeader.PointerToSymbolTable);
|
||
showint(pe->FileHeader.NumberOfSymbols);
|
||
showshort(pe->FileHeader.SizeOfOptionalHeader);
|
||
show(".short",
|
||
firstnonnull(RecreateFlags(kNtImageCharacteristicNames,
|
||
pe->FileHeader.Characteristics),
|
||
format(b1, "%#hx", pe->FileHeader.Characteristics)),
|
||
"pe->FileHeader.Characteristics");
|
||
printf("\n");
|
||
showpeoptionalheader(pecheckaddress(mz, mzsize, &pe->OptionalHeader,
|
||
pe->FileHeader.SizeOfOptionalHeader));
|
||
}
|
||
|
||
static void showall(void) {
|
||
startfile();
|
||
showmzheader();
|
||
showdosstub();
|
||
if (mz->e_lfanew) {
|
||
showpeheader(pecomputerva(mz, mzsize, mz->e_lfanew,
|
||
sizeof(struct NtImageFileHeader)));
|
||
}
|
||
}
|
||
|
||
int main(int argc, char *argv[]) {
|
||
int64_t fd;
|
||
struct stat st[1];
|
||
if (argc != 2) fprintf(stderr, "usage: %s FILE\n", argv[0]), exit(1);
|
||
if ((fd = open((path = argv[1]), O_RDONLY)) == -1 || fstat(fd, st) == -1 ||
|
||
(mz = mmap(NULL, (mzsize = st->st_size), PROT_READ, MAP_SHARED, fd, 0)) ==
|
||
MAP_FAILED) {
|
||
fprintf(stderr, "error: %'s %m\n", path);
|
||
exit(1);
|
||
}
|
||
if (mz->e_magic != kNtImageDosSignature) {
|
||
fprintf(stderr, "error: %'s not a dos executable\n", path);
|
||
exit(1);
|
||
}
|
||
showall();
|
||
munmap(mz, mzsize);
|
||
close(fd);
|
||
return 0;
|
||
}
|