From 6aeaabaed242e3ce10b5cab2366aee70493dffc1 Mon Sep 17 00:00:00 2001 From: Ophelia Beatrice de Sica Date: Fri, 12 May 2023 19:36:13 +0200 Subject: [PATCH 1/4] Add premake and prepare main executable --- .github/workflows/win-artifact-build.yml | 39 ++ Makefile | 95 +++-- completion/bash/readpe | 13 +- include/common.h | 6 +- include/peldd.h | 40 ++ include/peres.h | 48 +++ include/pesec.h | 55 +++ include/pestr.h | 46 +++ include/readpe.h | 55 +++ lib/libpe/Makefile | 324 ++++++++------- lib/libudis86/libudis86/Makefile | 159 ++++++++ premake5.lua | 71 ++++ src/Makefile | 394 ++++++++++-------- src/main.c | 492 +++++++++++++++++++++++ src/main.h | 51 +++ src/ofs2rva.c | 5 +- src/pedis.c | 7 +- src/pehash.c | 3 +- src/peldd.c | 8 +- src/pepack.c | 3 +- src/peres.c | 14 +- src/pescan.c | 5 +- src/pesec.c | 104 +++-- src/pestr.c | 78 ++-- src/readpe.c | 74 ++-- src/rva2ofs.c | 3 +- 26 files changed, 1690 insertions(+), 502 deletions(-) create mode 100644 .github/workflows/win-artifact-build.yml create mode 100644 include/peldd.h create mode 100644 include/peres.h create mode 100644 include/pesec.h create mode 100644 include/pestr.h create mode 100644 include/readpe.h create mode 100644 lib/libudis86/libudis86/Makefile create mode 100644 premake5.lua create mode 100644 src/main.c create mode 100644 src/main.h diff --git a/.github/workflows/win-artifact-build.yml b/.github/workflows/win-artifact-build.yml new file mode 100644 index 00000000..f6238f50 --- /dev/null +++ b/.github/workflows/win-artifact-build.yml @@ -0,0 +1,39 @@ +name: win-artifact-build + +on: + push: + branches: [ build ] + +jobs: + build-win64: + + runs-on: windows-latest + + steps: + + - name: Install Cygwin + # You may pin to the exact commit or the version. + # uses: egor-tensin/setup-cygwin@4f96f9fecb8c952fa32ff791b0a77d93d5191bb4 + uses: egor-tensin/setup-cygwin@v3 + with: + platform: x64 # optional, default is x64 + install-dir: c:\tools\cygwin # optional, default is C:\tools\cygwin + packages: gcc-core binutils make zip libssl-devel # optional + + - name: Checkout with submodules + uses: actions/checkout@v3 + with: + submodules: 'true' + + - name: Compile + run: make + + - name: Compile Windows-only tools and create a ZIP package + run: make zip + + - name: Upload Artifact + uses: actions/upload-artifact@v3 + with: + name: win-build + path: ./*.zip + retention-days: 1 diff --git a/Makefile b/Makefile index 51637e9b..40e9c077 100644 --- a/Makefile +++ b/Makefile @@ -1,27 +1,68 @@ -LIBPE_DIR = lib/libpe -PEV_DIR = src -VERSION = 0.82 -ZIPDIR = pev-$(VERSION)-win -ZIPFILE = $(ZIPDIR).zip - -all: -%: - cd $(LIBPE_DIR) && $(MAKE) $@ - cd $(PEV_DIR) && $(MAKE) $@ - -# Cygwin only -zip: - cd $(PEV_DIR)/windows && $(MAKE) - mkdir -p $(ZIPDIR)/plugins - cp src/build/plugins/*.dll $(ZIPDIR)/plugins/ - echo -ne "plugins_dir=plugins\r\n" > $(ZIPDIR)/pev.conf - cp $(PEV_DIR)/userdb.txt $(ZIPDIR) - cp lib/libpe/libpe.dll $(ZIPDIR)/ - cp /usr/bin/cygwin1.dll $(ZIPDIR)/ - cp /usr/bin/cygcrypto-1*.dll $(ZIPDIR)/ - cp /usr/bin/cygz.dll $(ZIPDIR)/ - cp README.md $(ZIPDIR)/ - cp $(PEV_DIR)/build/*.exe $(ZIPDIR)/ - cp $(PEV_DIR)/windows/run.bat $(ZIPDIR)/ - zip -r $(ZIPFILE) $(ZIPDIR)/* - rm -rf $(ZIPDIR) +# Alternative GNU Make workspace makefile autogenerated by Premake + +ifndef config + config=debug +endif + +ifndef verbose + SILENT = @ +endif + +ifeq ($(config),debug) + pe_config = debug + udis86_config = debug + readpe_config = debug + +else ifeq ($(config),release) + pe_config = release + udis86_config = release + readpe_config = release + +else + $(error "invalid configuration $(config)") +endif + +PROJECTS := pe udis86 readpe + +.PHONY: all clean help $(PROJECTS) + +all: $(PROJECTS) + +pe: +ifneq (,$(pe_config)) + @echo "==== Building pe ($(pe_config)) ====" + @${MAKE} --no-print-directory -C lib/libpe -f Makefile config=$(pe_config) +endif + +udis86: +ifneq (,$(udis86_config)) + @echo "==== Building udis86 ($(udis86_config)) ====" + @${MAKE} --no-print-directory -C lib/libudis86/libudis86 -f Makefile config=$(udis86_config) +endif + +readpe: pe udis86 +ifneq (,$(readpe_config)) + @echo "==== Building readpe ($(readpe_config)) ====" + @${MAKE} --no-print-directory -C src -f Makefile config=$(readpe_config) +endif + +clean: + @${MAKE} --no-print-directory -C lib/libpe -f Makefile clean + @${MAKE} --no-print-directory -C lib/libudis86/libudis86 -f Makefile clean + @${MAKE} --no-print-directory -C src -f Makefile clean + +help: + @echo "Usage: make [config=name] [target]" + @echo "" + @echo "CONFIGURATIONS:" + @echo " debug" + @echo " release" + @echo "" + @echo "TARGETS:" + @echo " all (default)" + @echo " clean" + @echo " pe" + @echo " udis86" + @echo " readpe" + @echo "" + @echo "For more information, see https://github.com/premake/premake-core/wiki" \ No newline at end of file diff --git a/completion/bash/readpe b/completion/bash/readpe index d36e4b43..9eb88074 100644 --- a/completion/bash/readpe +++ b/completion/bash/readpe @@ -1,5 +1,15 @@ #!/usr/bin/env bash -complete -F _longopt readpe + +function _complete_readpe () { + echo $COMP_LINE + # local _comp=$COMP_LINE + # local _comp=$("${COMP_LINE[@]::${#COMP_LINE[@]}-1} --complete") + local _comp=$($COMP_LINE "--complete") + # printf "headers directories exports" + COMPREPLY=($_comp) +} + +complete -F _complete_readpe readpe complete -F _longopt pedis complete -F _longopt pehash complete -F _longopt peldd @@ -9,3 +19,4 @@ complete -F _longopt pescan complete -F _longopt pesec complete -F _longopt pestr + diff --git a/include/common.h b/include/common.h index 2ba551bf..d50d83c2 100644 --- a/include/common.h +++ b/include/common.h @@ -59,7 +59,11 @@ #define MAX_MSG 81 #define MAX_PATH 256 -#define VERSION "0.82" + +#ifndef VERSION +#define VERSION "1.0" +#endif + #define TOOLKIT "from pev " VERSION " toolkit" #define COPY \ "License GPLv2+: GNU GPL version 2 or later .\n" \ diff --git a/include/peldd.h b/include/peldd.h new file mode 100644 index 00000000..2d55f964 --- /dev/null +++ b/include/peldd.h @@ -0,0 +1,40 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + Copyright (C) 2023 readpe authors + + 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, either version 2 of the License, or + (at your option) any later version. + + 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, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + +#pragma once + +#include + +void print_dependencies(pe_ctx_t *ctx); + diff --git a/include/peres.h b/include/peres.h new file mode 100644 index 00000000..b8b46e7e --- /dev/null +++ b/include/peres.h @@ -0,0 +1,48 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + Copyright (C) 2023 readpe authors + + 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, either version 2 of the License, or + (at your option) any later version. + + 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, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + +#pragma once + +#include +#include +#include +#include +#include + +void peres_show_nodes(pe_ctx_t *ctx, const pe_resource_node_t *node); +void peres_show_stats(const pe_resource_node_t *node); +void peres_show_list(pe_ctx_t *ctx, const pe_resource_node_t *node); +void peres_save_all_resources(pe_ctx_t *ctx, const pe_resource_node_t *node, bool namedExtract); +void peres_show_version(pe_ctx_t *ctx, const pe_resource_node_t *node); + diff --git a/include/pesec.h b/include/pesec.h new file mode 100644 index 00000000..4431d0b1 --- /dev/null +++ b/include/pesec.h @@ -0,0 +1,55 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + Copyright (C) 2023 readpe authors + + 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, either version 2 of the License, or + (at your option) any later version. + + 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, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + +#pragma once + +#include "common.h" +#include +#include + +typedef enum { + CERT_FORMAT_TEXT = 1, + CERT_FORMAT_PEM = 2, + CERT_FORMAT_DER = 3 +} cert_format_e; + +typedef struct { + cert_format_e certoutform; + BIO *certout; +} certificate_settings; + +bool stack_cookies(pe_ctx_t *ctx); +void print_securities(pe_ctx_t *ctx); +void parse_certificates(const certificate_settings *options, pe_ctx_t *ctx); + diff --git a/include/pestr.h b/include/pestr.h new file mode 100644 index 00000000..e91d168c --- /dev/null +++ b/include/pestr.h @@ -0,0 +1,46 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + Copyright (C) 2023 readpe authors + + 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, either version 2 of the License, or + (at your option) any later version. + + 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, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + +#pragma once + +#include + +typedef struct { + unsigned short strsize; + bool offset; + bool section; +} string_settings; + +void print_strings( pe_ctx_t *ctx, const string_settings *options); + diff --git a/include/readpe.h b/include/readpe.h new file mode 100644 index 00000000..8babb96e --- /dev/null +++ b/include/readpe.h @@ -0,0 +1,55 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + readpe.h + + Copyright (C) 2023 readpe authors + + 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, either version 2 of the License, or + (at your option) any later version. + + 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, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + +#pragma once + +#include +#include +// #include "common.h" +// #include "output.h" + +void print_dos_header(pe_ctx_t *ctx); +void print_coff_header(pe_ctx_t *ctx); +void print_optional_header(pe_ctx_t *ctx); + +void print_directories(pe_ctx_t *ctx); +void print_imports(pe_ctx_t *ctx); +void print_exports(pe_ctx_t *ctx); + +void print_sections(pe_ctx_t *ctx); + +IMAGE_DATA_DIRECTORY **get_pe_directories(pe_ctx_t *ctx); + diff --git a/lib/libpe/Makefile b/lib/libpe/Makefile index 85547454..d218c1f9 100644 --- a/lib/libpe/Makefile +++ b/lib/libpe/Makefile @@ -1,169 +1,183 @@ -####### Platform specifics - -# cut is necessary for Cygwin -PLATFORM_OS := $(shell uname | cut -d_ -f1) - -####### Makefile Conventions - Directory variables - -srcdir = . -prefix = /usr/local -exec_prefix = $(prefix) -sysconfdir = $(prefix)/etc -includedir = $(prefix)/include -datarootdir = $(prefix)/share -localstatedir = $(prefix)/var -bindir = $(exec_prefix)/bin -libdir = $(exec_prefix)/lib -libexecdir = $(exec_prefix)/libexec -sbindir = $(exec_prefix)/sbin -datadir = $(datarootdir) -docdir = $(datarootdir)/doc/pev -infodir = $(datarootdir)/info -localedir = $(datarootdir)/locale - -mandir = $(datarootdir)/man -manext = .1 -man1dir = $(mandir)/man1 -man1ext = .1 - -####### Makefile Conventions - Utilities - -CC ?= gcc -LINK = $(CC) -CHK_DIR_EXISTS = test -d -CHK_FILE_EXISTS = test -f -INSTALL = install -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_PROGRAM = $(INSTALL) -SYMLINK = ln -sf -MKDIR = mkdir -p -RM = rm -f -RM_DIR = rm -rf - -ifeq ($(PLATFORM_OS), Darwin) - STRIP = strip -x -else - STRIP = strip --strip-unneeded -endif - -####### Compiler options - -override CFLAGS += \ - -O2 -ffast-math \ - -I"./include" \ - -fPIC \ - -W -Wall -Wextra -pedantic -std=c99 -c -override CPPFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -#override LDFLAGS += -lssl -lcrypto -LIBS = -lssl -lcrypto -lm - -# --- FIX: -fPIC is necessary to ALL shared objects! Changed above. -#ifneq ($(PLATFORM_OS), CYGWIN) -# override CFLAGS += -fPIC -#endif - -VERSION = 0.82 -LIBNAME = libpe +# Alternative GNU Make project makefile autogenerated by Premake -SRC_DIRS = $(srcdir) $(srcdir)/libfuzzy - -libpe_BUILDDIR = $(CURDIR)/build -libpe_SRCS_FILTER = $(sort $(wildcard ${dir}/*.c)) -libpe_SRCS = $(foreach dir, ${SRC_DIRS}, ${libpe_SRCS_FILTER}) -libpe_OBJS = $(addprefix ${libpe_BUILDDIR}/, $(addsuffix .o, $(basename ${libpe_SRCS}))) +ifndef config + config=debug +endif -####### Build rules +ifndef verbose + SILENT = @ +endif -.PHONY : libpe install strip-binaries install-strip uninstall clean +.PHONY: clean prebuild -all: libpe +SHELLTYPE := posix +ifeq (.exe,$(findstring .exe,$(ComSpec))) + SHELLTYPE := msdos +endif -# FIX: WARNING.. ld expects -l option at the END of the command line or after the object files. -# From gcc's documentation: -# -# It makes a difference where in the command you write this option; the linker searches and processes -# libraries and object files in the order they are specified. Thus, ‘foo.o -lz bar.o’ searches library -# ‘z’ after file foo.o but before bar.o. If bar.o refers to functions in ‘z’, those functions -# may not be loaded. -# +# Configurations +# ############################################# + +RESCOMP = windres +DEFINES += -D_GNU_SOURCE +INCLUDES += -Iinclude +FORCE_INCLUDE += +ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) +ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) +LIBS += -lcrypto -lm +LDDEPS += +define PREBUILDCMDS +endef +define PRELINKCMDS +endef +define POSTBUILDCMDS +endef + +ifeq ($(config),debug) +TARGETDIR = ../../build/Debug +TARGET = $(TARGETDIR)/libpe.a +OBJDIR = obj/Debug +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) +ALL_LDFLAGS += $(LDFLAGS) -s +LINKCMD = $(AR) -rcs "$@" $(OBJECTS) + +else ifeq ($(config),release) +TARGETDIR = ../../build/Release +TARGET = $(TARGETDIR)/libpe.so +OBJDIR = obj/Release +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -fPIC +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -fPIC +ALL_LDFLAGS += $(LDFLAGS) -shared -Wl,-soname=libpe.so -s +LINKCMD = $(CC) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) -libpe: CPPFLAGS += -D_GNU_SOURCE -ifeq ($(PLATFORM_OS), CYGWIN) -libpe: CPPFLAGS += -D_XOPEN_SOURCE=600 endif -libpe: $(libpe_OBJS) -ifeq ($(PLATFORM_OS), Linux) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS) -else ifeq ($(PLATFORM_OS), NetBSD) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS) -else ifeq ($(PLATFORM_OS), FreeBSD) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS) -else ifeq ($(PLATFORM_OS), OpenBSD) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS) -else ifeq ($(PLATFORM_OS), GNU) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS) -else ifeq ($(PLATFORM_OS), GNU/kFreeBSD) - $(LINK) -shared -Wl,-soname,$(LIBNAME).so.1 $(LDFLAGS) -o $(LIBNAME).so $^ $(LIBS) -else ifeq ($(PLATFORM_OS), Darwin) - $(LINK) -headerpad_max_install_names -dynamiclib \ - -flat_namespace -install_name $(LIBNAME).$(VERSION).dylib \ - -current_version $(VERSION) -compatibility_version $(VERSION) \ - $(LDFLAGS) -o $(LIBNAME).dylib $^ $(LIBS) -else ifeq ($(PLATFORM_OS), CYGWIN) - $(LINK) -shared -o $(LIBNAME).dll $^ $(LDFLAGS) $(LIBS) + +# Per File Configurations +# ############################################# + + +# File sets +# ############################################# + +GENERATED := +OBJECTS := + +GENERATED += $(OBJDIR)/edit_dist.o +GENERATED += $(OBJDIR)/error.o +GENERATED += $(OBJDIR)/exports.o +GENERATED += $(OBJDIR)/fuzzy.o +GENERATED += $(OBJDIR)/hashes.o +GENERATED += $(OBJDIR)/imports.o +GENERATED += $(OBJDIR)/misc.o +GENERATED += $(OBJDIR)/pe.o +GENERATED += $(OBJDIR)/resources.o +GENERATED += $(OBJDIR)/utils.o +OBJECTS += $(OBJDIR)/edit_dist.o +OBJECTS += $(OBJDIR)/error.o +OBJECTS += $(OBJDIR)/exports.o +OBJECTS += $(OBJDIR)/fuzzy.o +OBJECTS += $(OBJDIR)/hashes.o +OBJECTS += $(OBJDIR)/imports.o +OBJECTS += $(OBJDIR)/misc.o +OBJECTS += $(OBJDIR)/pe.o +OBJECTS += $(OBJDIR)/resources.o +OBJECTS += $(OBJDIR)/utils.o + +# Rules +# ############################################# + +all: $(TARGET) + @: + +$(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR) + $(PRELINKCMDS) + @echo Linking pe + $(SILENT) $(LINKCMD) + $(POSTBUILDCMDS) + +$(TARGETDIR): + @echo Creating $(TARGETDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(TARGETDIR) +else + $(SILENT) mkdir $(subst /,\\,$(TARGETDIR)) endif -$(libpe_BUILDDIR)/%.o: %.c - @$(CHK_DIR_EXISTS) $(dir $@) || $(MKDIR) $(dir $@) - $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< - -install: installdirs -ifeq ($(PLATFORM_OS), Linux) - $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION) - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1 -else ifeq ($(PLATFORM_OS), NetBSD) - $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION) - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1 -else ifeq ($(PLATFORM_OS), FreeBSD) - $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION) - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1 -else ifeq ($(PLATFORM_OS), OpenBSD) - $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION) - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1 -else ifeq ($(PLATFORM_OS), GNU) - $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION) - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1 -else ifeq ($(PLATFORM_OS), GNU/kFreeBSD) - $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).so $(DESTDIR)$(libdir)/$(LIBNAME).so.$(VERSION) - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).so.$(VERSION) $(LIBNAME).so.1 -else ifeq ($(PLATFORM_OS), Darwin) - $(INSTALL_DATA) $(INSTALL_FLAGS) $(LIBNAME).dylib $(DESTDIR)$(libdir)/$(LIBNAME).$(VERSION).dylib - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).$(VERSION).dylib $(LIBNAME).dylib - cd $(DESTDIR)$(libdir); $(SYMLINK) $(LIBNAME).$(VERSION).dylib $(LIBNAME).1.dylib -else ifeq ($(PLATFORM_OS), CYGWIN) - # TODO +$(OBJDIR): + @echo Creating $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) endif -installdirs: - @$(CHK_DIR_EXISTS) $(DESTDIR) || $(MKDIR) $(DESTDIR) - @$(CHK_DIR_EXISTS) $(DESTDIR)$(libdir) || $(MKDIR) $(DESTDIR)$(libdir) +clean: + @echo Cleaning pe +ifeq (posix,$(SHELLTYPE)) + $(SILENT) rm -f $(TARGET) + $(SILENT) rm -rf $(GENERATED) + $(SILENT) rm -rf $(OBJDIR) +else + $(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET)) + $(SILENT) if exist $(subst /,\\,$(GENERATED)) del /s /q $(subst /,\\,$(GENERATED)) + $(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR)) +endif -install-strip: INSTALL_FLAGS += -s -install-strip: install +prebuild: | $(OBJDIR) + $(PREBUILDCMDS) + +ifneq (,$(PCH)) +$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER) +$(GCH): $(PCH) | prebuild + @echo $(notdir $<) + $(SILENT) $(CC) -x c-header $(ALL_CFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<" +$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) touch "$@" +else + $(SILENT) echo $null >> "$@" +endif +else +$(OBJECTS): | prebuild +endif -uninstall: - $(RM) $(DESTDIR)$(libdir)/$(LIBNAME).so* \ - $(DESTDIR)$(libdir)/$(LIBNAME)*.dylib -clean: - $(RM_DIR) $(libpe_BUILDDIR) - $(RM) $(LIBNAME)*.o \ - $(LIBNAME)*.so \ - $(LIBNAME)*.dylib \ - $(LIBNAME)*.dll +# File Rules +# ############################################# + +$(OBJDIR)/error.o: error.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/exports.o: exports.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/hashes.o: hashes.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/imports.o: imports.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/edit_dist.o: libfuzzy/edit_dist.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/fuzzy.o: libfuzzy/fuzzy.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/misc.o: misc.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/pe.o: pe.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/resources.o: resources.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/utils.o: utils.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + +-include $(OBJECTS:%.o=%.d) +ifneq (,$(PCH)) + -include $(PCH_PLACEHOLDER).d +endif \ No newline at end of file diff --git a/lib/libudis86/libudis86/Makefile b/lib/libudis86/libudis86/Makefile new file mode 100644 index 00000000..d341b19e --- /dev/null +++ b/lib/libudis86/libudis86/Makefile @@ -0,0 +1,159 @@ +# Alternative GNU Make project makefile autogenerated by Premake + +ifndef config + config=debug +endif + +ifndef verbose + SILENT = @ +endif + +.PHONY: clean prebuild + +SHELLTYPE := posix +ifeq (.exe,$(findstring .exe,$(ComSpec))) + SHELLTYPE := msdos +endif + +# Configurations +# ############################################# + +RESCOMP = windres +DEFINES += -DHAVE_STRING_H=1 +INCLUDES += +FORCE_INCLUDE += +ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) +ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) +LIBS += +LDDEPS += +ALL_LDFLAGS += $(LDFLAGS) -s +LINKCMD = $(AR) -rcs "$@" $(OBJECTS) +define PREBUILDCMDS +endef +define PRELINKCMDS +endef +define POSTBUILDCMDS +endef + +ifeq ($(config),debug) +TARGETDIR = ../../../build/Debug +TARGET = $(TARGETDIR)/libudis86.a +OBJDIR = obj/Debug + +else ifeq ($(config),release) +TARGETDIR = ../../../build/Release +TARGET = $(TARGETDIR)/libudis86.a +OBJDIR = obj/Release + +endif + +# Per File Configurations +# ############################################# + + +# File sets +# ############################################# + +GENERATED := +OBJECTS := + +GENERATED += $(OBJDIR)/decode.o +GENERATED += $(OBJDIR)/itab.o +GENERATED += $(OBJDIR)/syn-att.o +GENERATED += $(OBJDIR)/syn-intel.o +GENERATED += $(OBJDIR)/syn.o +GENERATED += $(OBJDIR)/udis86.o +OBJECTS += $(OBJDIR)/decode.o +OBJECTS += $(OBJDIR)/itab.o +OBJECTS += $(OBJDIR)/syn-att.o +OBJECTS += $(OBJDIR)/syn-intel.o +OBJECTS += $(OBJDIR)/syn.o +OBJECTS += $(OBJDIR)/udis86.o + +# Rules +# ############################################# + +all: $(TARGET) + @: + +$(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR) + $(PRELINKCMDS) + @echo Linking udis86 + $(SILENT) $(LINKCMD) + $(POSTBUILDCMDS) + +$(TARGETDIR): + @echo Creating $(TARGETDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(TARGETDIR) +else + $(SILENT) mkdir $(subst /,\\,$(TARGETDIR)) +endif + +$(OBJDIR): + @echo Creating $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + +clean: + @echo Cleaning udis86 +ifeq (posix,$(SHELLTYPE)) + $(SILENT) rm -f $(TARGET) + $(SILENT) rm -rf $(GENERATED) + $(SILENT) rm -rf $(OBJDIR) +else + $(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET)) + $(SILENT) if exist $(subst /,\\,$(GENERATED)) del /s /q $(subst /,\\,$(GENERATED)) + $(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR)) +endif + +prebuild: | $(OBJDIR) + $(PREBUILDCMDS) + +ifneq (,$(PCH)) +$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER) +$(GCH): $(PCH) | prebuild + @echo $(notdir $<) + $(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<" +$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) touch "$@" +else + $(SILENT) echo $null >> "$@" +endif +else +$(OBJECTS): | prebuild +endif + + +# File Rules +# ############################################# + +$(OBJDIR)/decode.o: decode.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/itab.o: itab.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/syn-att.o: syn-att.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/syn-intel.o: syn-intel.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/syn.o: syn.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/udis86.o: udis86.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + +-include $(OBJECTS:%.o=%.d) +ifneq (,$(PCH)) + -include $(PCH_PLACEHOLDER).d +endif \ No newline at end of file diff --git a/premake5.lua b/premake5.lua new file mode 100644 index 00000000..3e02701d --- /dev/null +++ b/premake5.lua @@ -0,0 +1,71 @@ +workspace "readpe" + configurations { "Debug", "Release" } + +project "pe" + language "C" + location "lib/libpe" + includedirs { "lib/libpe/include" } + targetdir "build/%{cfg.buildcfg}" + files { "lib/libpe/**.h", "lib/libpe/**.c" } + defines { + "_GNU_SOURCE", + } + links { "crypto" } + + filter { "system:linux" } + links { "m" } + + filter "Debug" + kind "StaticLib" + + filter "Release" + kind "SharedLib" + +project "udis86" + kind "StaticLib" + location "lib/libudis86/libudis86" + targetdir "build/%{cfg.buildcfg}" + files { "lib/libudis86/libudis86/*.h", "lib/libudis86/libudis86/*.c" } + defines { "HAVE_STRING_H=1" } + +-- TODO: ZIP Packing +project "readpe" + kind "ConsoleApp" + language "C" + location "src" + includedirs { "include", "lib/libpe/include", "lib" } + targetdir "build/%{cfg.buildcfg}" + + files { "src/*.h", "src/*.c", "src/compat/*.c" } + -- removefiles { "src/ofs2rva.c", "src/pedis.c", "src/pehash.c", "src/pepack.c", "src/peres.c", "src/pescan.c", "src/pesec.c", "src/readpe.c", "src/rva2ofs.c" } + + defines { + "_GNU_SOURCE", + "SHAREDIR=\"\"", + "PLUGINSDIR=\"pev/plugins\"" + } + + links { "pe", "udis86", "crypto" } + + warnings "Extra" + flags { "LinkTimeOptimization" } + + filter { "system:linux" } + links { "m" } + + filter "configurations:Debug" + defines { "DEBUG" } + enablewarnings { + "pedantic", + "shadow", + "undef", + "double-promotion", + "format=2", + "format-security", + "conversion" + } + symbols "On" + + filter "configurations:Release" + defines { "NDEBUG" } + optimize "Full" diff --git a/src/Makefile b/src/Makefile index b22251a3..4e62995b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,187 +1,235 @@ -####### Platform specifics - -# cut is necessary for Cygwin -export PLATFORM_OS := $(shell uname | cut -d_ -f1) - -####### Makefile Conventions - Directory variables - -srcdir = . -prefix = /usr/local - -exec_prefix = $(prefix) -sysconfdir = $(prefix)/etc -includedir = $(prefix)/include -datarootdir = $(prefix)/share -localstatedir = $(prefix)/var - -bindir = $(exec_prefix)/bin -libdir = $(exec_prefix)/lib -libexecdir = $(exec_prefix)/libexec -sbindir = $(exec_prefix)/sbin - -datadir = $(datarootdir) -docdir = $(datarootdir)/doc/pev -infodir = $(datarootdir)/info -localedir = $(datarootdir)/locale - -mandir = $(datarootdir)/man -manext = .1 -man1dir = $(mandir)/man1 -man1ext = .1 - -export pluginsdir = $(libdir)/pev/plugins - -####### Makefile Conventions - Utilities - -export CC ?= gcc -export LINK = $(CC) -export CHK_DIR_EXISTS = test -d -export CHK_FILE_EXISTS = test -f -export INSTALL = install -export INSTALL_DATA = ${INSTALL} -m 644 -export INSTALL_PROGRAM = ${INSTALL} -export SYMLINK = ln -sf -export MKDIR = mkdir -p -export RM = rm -f -export RM_DIR = rm -rf -ifeq ($(PLATFORM_OS), Darwin) - export STRIP = strip -x -else - export STRIP = strip --strip-unneeded -endif - -####### Compiler options - -override LDFLAGS += -L$(LIBPE) -lpe -lcrypto -lssl -ldl -lm -override CFLAGS += -O2 -ffast-math -I$(LIBPE)/include -I"../include" -W -Wall -Wextra -Wno-implicit-fallthrough -std=c99 -pedantic - -# To compile for production define the symbol NDEBUG before invoking this makefile. -override CPPFLAGS += \ - -D_GNU_SOURCE \ - -DSHAREDIR="\"$(SHAREDIR)"\" \ - -DPLUGINSDIR="\"$(pluginsdir)"\" +# Alternative GNU Make project makefile autogenerated by Premake -# Some gcc/clang builds (depends on the distro) already define _FORTIFY_SOURCE internally, so we -# only define it if it has not been already. This avoids redefinition warnings and weakening -# those distros' hardening settings. -ifneq ($(findstring _FORTIFY_SOURCE, $(CPPFLAGS)), _FORTIFY_SOURCE) - override CPPFLAGS += -D_FORTIFY_SOURCE=1 +ifndef config + config=debug endif -ifeq ($(PLATFORM_OS), Darwin) - # We disable warnings for deprecated declarations since Apple deprecated OpenSSL in Mac OS X 10.7 - override CFLAGS += -Wno-deprecated-declarations +ifndef verbose + SILENT = @ endif -ifeq ($(PLATFORM_OS), CYGWIN) - override CPPFLAGS += -D_XOPEN_SOURCE=600 -endif - -SRC_DIRS = $(srcdir) $(srcdir)/compat - -PROGS = readpe rva2ofs ofs2rva pehash pesec pescan pepack pestr pedis peres peldd -PLUGINS_DIR = $(srcdir)/plugins -SHAREDIR = $(datadir)/pev -export LIBPE = $(realpath $(srcdir)/../lib/libpe) -LIBUDIS86 = $(srcdir)/../lib/libudis86 -MANDIR = $(srcdir)/../doc/manpages - -export pev_BUILDDIR = ./build -pev_SRCS_FILTER = $(sort $(wildcard ${dir}/*.c)) -pev_SRCS = $(foreach dir, ${SRC_DIRS}, ${pev_SRCS_FILTER}) -pev_OBJS = $(addprefix ${pev_BUILDDIR}/, $(addsuffix .o, $(basename ${pev_SRCS}))) - -pev_COMMON_DEPS = \ - $(pev_BUILDDIR)/compat/strlcat.o \ - $(pev_BUILDDIR)/config.o \ - $(pev_BUILDDIR)/dylib.o \ - $(pev_BUILDDIR)/malloc_s.o \ - $(pev_BUILDDIR)/plugins.o \ - $(pev_BUILDDIR)/output_plugin.o \ - $(pev_BUILDDIR)/output.o \ - $(pev_BUILDDIR)/pev_api.o - -####### Build rules - -.PHONY: plugins install installdirs uninstall clean - -all: $(PROGS) plugins - -plugins: - cd $(PLUGINS_DIR) && $(MAKE) $@ - -ofs2rva: $(pev_BUILDDIR)/ofs2rva.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) - -pedis: CPPFLAGS += -DHAVE_STRING_H -pedis: CFLAGS += -I$(LIBUDIS86) -pedis: $(pev_BUILDDIR)/pedis.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) $(sort $(LIBUDIS86)/libudis86/*.c) - -pehash: $(pev_BUILDDIR)/pehash.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) - -pepack: $(pev_BUILDDIR)/pepack.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) - -peres: $(pev_BUILDDIR)/peres.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) +.PHONY: clean prebuild -pescan: LDFLAGS += -lm -pescan: $(pev_BUILDDIR)/pescan.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) - -pesec: LDFLAGS += -lcrypto -pesec: $(pev_BUILDDIR)/pesec.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) - -pestr: $(pev_BUILDDIR)/pestr.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) - -readpe: $(pev_BUILDDIR)/readpe.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) - -rva2ofs: $(pev_BUILDDIR)/rva2ofs.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) - -peldd: $(pev_BUILDDIR)/peldd.o $(pev_OBJS) - $(CC) $< -o $(pev_BUILDDIR)/$@ $(pev_COMMON_DEPS) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) +SHELLTYPE := posix +ifeq (.exe,$(findstring .exe,$(ComSpec))) + SHELLTYPE := msdos +endif -# Generic rule matching sources +# Configurations +# ############################################# + +RESCOMP = windres +INCLUDES += -I../include -I../lib/libpe/include -I../lib +FORCE_INCLUDE += +ALL_CPPFLAGS += $(CPPFLAGS) -MD -MP $(DEFINES) $(INCLUDES) +ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES) +LINKCMD = $(CC) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS) +define PREBUILDCMDS +endef +define PRELINKCMDS +endef +define POSTBUILDCMDS +endef + +ifeq ($(config),debug) +TARGETDIR = ../build/Debug +TARGET = $(TARGETDIR)/readpe +OBJDIR = obj/Debug +DEFINES += -D_GNU_SOURCE -DSHAREDIR=\"\" -DPLUGINSDIR=\"pev/plugins\" -DDEBUG +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -flto -g -Wall -Wextra -Wpedantic -Wshadow -Wundef -Wdouble-promotion -Wformat=2 -Wformat-security -Wconversion +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -flto -g -Wall -Wextra -Wpedantic -Wshadow -Wundef -Wdouble-promotion -Wformat=2 -Wformat-security -Wconversion +LIBS += ../build/Debug/libpe.a ../build/Debug/libudis86.a -lcrypto -lm +LDDEPS += ../build/Debug/libpe.a ../build/Debug/libudis86.a +ALL_LDFLAGS += $(LDFLAGS) -flto + +else ifeq ($(config),release) +TARGETDIR = ../build/Release +TARGET = $(TARGETDIR)/readpe +OBJDIR = obj/Release +DEFINES += -D_GNU_SOURCE -DSHAREDIR=\"\" -DPLUGINSDIR=\"pev/plugins\" -DNDEBUG +ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -flto -O3 -Wall -Wextra +ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -flto -O3 -Wall -Wextra +LIBS += ../build/Release/libpe.so ../build/Release/libudis86.a -lcrypto -lm +LDDEPS += ../build/Release/libpe.so ../build/Release/libudis86.a +ALL_LDFLAGS += $(LDFLAGS) -Wl,-rpath,'$$ORIGIN' -flto -s -$(pev_BUILDDIR)/%.o: %.c - @$(CHK_DIR_EXISTS) $(dir $@) || $(MKDIR) $(dir $@) - $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) $(INCPATH) +endif -install: installdirs - for prog in $(PROGS); do \ - $(INSTALL_PROGRAM) $(INSTALL_FLAGS) $(pev_BUILDDIR)/$$prog $(DESTDIR)$(bindir); \ - $(CHK_FILE_EXISTS) $(MANDIR)/$$prog$(man1ext) && \ - gzip -c -9 $(MANDIR)/$$prog$(man1ext) > $(DESTDIR)$(man1dir)/$$prog$(man1ext).gz || \ - echo -n; \ - done +# Per File Configurations +# ############################################# + + +# File sets +# ############################################# + +GENERATED := +OBJECTS := + +GENERATED += $(OBJDIR)/config.o +GENERATED += $(OBJDIR)/dylib.o +GENERATED += $(OBJDIR)/main.o +GENERATED += $(OBJDIR)/malloc_s.o +GENERATED += $(OBJDIR)/ofs2rva.o +GENERATED += $(OBJDIR)/output.o +GENERATED += $(OBJDIR)/output_plugin.o +GENERATED += $(OBJDIR)/pedis.o +GENERATED += $(OBJDIR)/pehash.o +GENERATED += $(OBJDIR)/peldd.o +GENERATED += $(OBJDIR)/pepack.o +GENERATED += $(OBJDIR)/peres.o +GENERATED += $(OBJDIR)/pescan.o +GENERATED += $(OBJDIR)/pesec.o +GENERATED += $(OBJDIR)/pestr.o +GENERATED += $(OBJDIR)/pev_api.o +GENERATED += $(OBJDIR)/plugins.o +GENERATED += $(OBJDIR)/readpe.o +GENERATED += $(OBJDIR)/rva2ofs.o +GENERATED += $(OBJDIR)/strlcat.o +OBJECTS += $(OBJDIR)/config.o +OBJECTS += $(OBJDIR)/dylib.o +OBJECTS += $(OBJDIR)/main.o +OBJECTS += $(OBJDIR)/malloc_s.o +OBJECTS += $(OBJDIR)/ofs2rva.o +OBJECTS += $(OBJDIR)/output.o +OBJECTS += $(OBJDIR)/output_plugin.o +OBJECTS += $(OBJDIR)/pedis.o +OBJECTS += $(OBJDIR)/pehash.o +OBJECTS += $(OBJDIR)/peldd.o +OBJECTS += $(OBJDIR)/pepack.o +OBJECTS += $(OBJDIR)/peres.o +OBJECTS += $(OBJDIR)/pescan.o +OBJECTS += $(OBJDIR)/pesec.o +OBJECTS += $(OBJDIR)/pestr.o +OBJECTS += $(OBJDIR)/pev_api.o +OBJECTS += $(OBJDIR)/plugins.o +OBJECTS += $(OBJDIR)/readpe.o +OBJECTS += $(OBJDIR)/rva2ofs.o +OBJECTS += $(OBJDIR)/strlcat.o + +# Rules +# ############################################# + +all: $(TARGET) + @: + +$(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR) + $(PRELINKCMDS) + @echo Linking readpe + $(SILENT) $(LINKCMD) + $(POSTBUILDCMDS) + +$(TARGETDIR): + @echo Creating $(TARGETDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(TARGETDIR) +else + $(SILENT) mkdir $(subst /,\\,$(TARGETDIR)) +endif - $(INSTALL_DATA) $(srcdir)/userdb.txt $(DESTDIR)$(SHAREDIR) - cd $(PLUGINS_DIR) && $(MAKE) $@ +$(OBJDIR): + @echo Creating $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif -install-strip: INSTALL_FLAGS += -s -install-strip: install +clean: + @echo Cleaning readpe +ifeq (posix,$(SHELLTYPE)) + $(SILENT) rm -f $(TARGET) + $(SILENT) rm -rf $(GENERATED) + $(SILENT) rm -rf $(OBJDIR) +else + $(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET)) + $(SILENT) if exist $(subst /,\\,$(GENERATED)) del /s /q $(subst /,\\,$(GENERATED)) + $(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR)) +endif -installdirs: - @$(CHK_DIR_EXISTS) $(DESTDIR) || $(MKDIR) $(DESTDIR) - @$(CHK_DIR_EXISTS) $(DESTDIR)$(bindir) || $(MKDIR) $(DESTDIR)$(bindir) - @$(CHK_DIR_EXISTS) $(DESTDIR)$(man1dir) || $(MKDIR) $(DESTDIR)$(man1dir) - @$(CHK_DIR_EXISTS) $(DESTDIR)$(SHAREDIR) || $(MKDIR) $(DESTDIR)$(SHAREDIR) +prebuild: | $(OBJDIR) + $(PREBUILDCMDS) + +ifneq (,$(PCH)) +$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER) +$(GCH): $(PCH) | prebuild + @echo $(notdir $<) + $(SILENT) $(CC) -x c-header $(ALL_CFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<" +$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) touch "$@" +else + $(SILENT) echo $null >> "$@" +endif +else +$(OBJECTS): | prebuild +endif -uninstall: - for prog in $(PROGS); do \ - $(RM) $(DESTDIR)$(bindir)/$$prog; \ - $(RM) $(DESTDIR)$(man1dir)/$$prog$(man1ext).gz; \ - done - $(RM_DIR) $(DESTDIR)$(SHAREDIR) - cd $(PLUGINS_DIR) && $(MAKE) $@ -clean: - $(RM_DIR) $(pev_BUILDDIR) - $(RM) $(PROGS) - cd $(PLUGINS_DIR) && $(MAKE) $@ +# File Rules +# ############################################# + +$(OBJDIR)/strlcat.o: compat/strlcat.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/config.o: config.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/dylib.o: dylib.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/main.o: main.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/malloc_s.o: malloc_s.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/ofs2rva.o: ofs2rva.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/output.o: output.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/output_plugin.o: output_plugin.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/pedis.o: pedis.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/pehash.o: pehash.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/peldd.o: peldd.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/pepack.o: pepack.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/peres.o: peres.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/pescan.o: pescan.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/pesec.o: pesec.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/pestr.o: pestr.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/pev_api.o: pev_api.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/plugins.o: plugins.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/readpe.o: readpe.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" +$(OBJDIR)/rva2ofs.o: rva2ofs.c + @echo "$(notdir $<)" + $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + +-include $(OBJECTS:%.o=%.d) +ifneq (,$(PCH)) + -include $(PCH_PLACEHOLDER).d +endif \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..a8f6b73d --- /dev/null +++ b/src/main.c @@ -0,0 +1,492 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + main.c - main executable entry + + Copyright (C) 2023 readpe authors + + 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, either version 2 of the License, or + (at your option) any later version. + + 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, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + +#include "main.h" +#include "common.h" +#include "output.h" +#include "peldd.h" +#include "peres.h" +#include "pesec.h" +#include "pestr.h" +#include "readpe.h" +#include +#include +#include +#include +#include +#include + +enum ARGUMENTS { + ARG_NONE = 0, + + ARG_HELP = 1, + ARG_COMPLETE = 2, + ARG_VERSION = 'V', + + MODE_START = 1000, + MODE_HEADER = 1000, + MODE_HEADER_DOS, + MODE_HEADER_COFF, + MODE_HEADER_OPTIONAL, + MODE_DIRECTORY = 1100, + MODE_EXPORTS, + MODE_IMPORTS, + MODE_RESOURCES, // -- peres + // MODE_EXCEPTIONS, + MODE_CERTIFICATES, // not part of image / -- pesec + // MODE_BASE_RELOCATIONS, + // MODE_DEBUG, + // MODE_ARCHITECTURE, + // MODE_GLOBAL_PTR, + // MODE_TLS, + // MODE_LOAD_CONFIGS, + // MODE_BOUND_IMPORT, + // MODE_IAT, + // MODE_DELAY_IMPORT_DESCRIPTOR, + // MODE_CLR_RUNTIME_HEADER, + MODE_SECURITY, // TODO: Duplicate of MODE_CERTIFICATES, + MODE_SECTION = 1200, + MODE_LIBRARY = 1300, // -- peldd + MODE_STRINGS = 1400, // -- pestr + // MODE_STRINGS_ASCII, + // MODE_STRINGS_UNICODE, + MODE_END, + + COMMAND_SCAN = 2000, // -- pescan + COMMAND_HASH = 2100, // -- pehash + // COMMAND_HASH_MD5, + // COMMAND_HASH_SHA1, + // COMMAND_HASH_SHA256, + // COMMAND_HASH_SSDEEP, + // COMMAND_HASH_IMPHASH, + + COMMAND_DISASSAMBLE = 100000, // -- pedis + COMMAND_PACK, // -- pepack + COMMAND_TRANSFORM = 101000, + // COMMAND_ADDRESSING_RELATIVE, // -- ofs2rva + // COMMAND_ADDRESSING_OFFSET, // -- rva2ofs +}; + +enum HASH_ALGO { + HASH_MD5, + HASH_SHA1, + HASH_SHA256, + HASH_SSDEEP, + HASH_IMPHASH, + HASH_ALL +}; + +static struct argument_options_t { + unsigned int list : 1; + unsigned int all : 1; +} argument_options; + +typedef struct { + const char *const name; + const void *const sub; + const int has_arg; + const int *const flag; + const int val; +} mode_option; + +int getopt_mode(int argc, char *const argv[], const mode_option **mode_options, + int *restrict select, int *restrict index) { + if (*index >= argc) return -1; + char *const arg = argv[*index]; + *index += 1; + + int val = 0; + + for (int i = 0; (*mode_options)[i].name != NULL; ++i) { + mode_option opt = (*mode_options)[i]; + // printf("%s %s\n", arg, opt.name); + if (strcmp(arg, opt.name) == 0) { + val = opt.val; + if (opt.sub != NULL) { + *mode_options = opt.sub; + } + if (val >= MODE_START) { + *select = val; + } + break; + } + } + + return val; +} + +// GNU compliant version output +void version(void) { + printf("readpe %s\n" + "Copyright (C) 2023 readpe authors\n" + "License GPLv2+: GNU GPL version 2 or later " + ".\n" + "This is free software: " + "you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n", + VERSION); + exit(EXIT_SUCCESS); +} + +// GNU compliant help output +void help(void) { + printf("Usage: readpe [] [] [] []\n"); + + printf("\nReport bugs to: https://github.com/mentebinaria/readpe/issues\n"); + exit(EXIT_SUCCESS); +} + +void complete(const mode_option *opts) { + size_t i = 0; + const mode_option *c; + while ((c = &opts[i])) { + if (c->name == NULL) break; + printf("%s ", c->name); + ++i; + } + printf("\n"); + exit(EXIT_SUCCESS); +} + +void usage(void) {} + +int main(int argc, char *argv[]) { + const char *tmp_name = strrchr(argv[0], '/'); + // If no '/' in caller (called from env) we set it to the original caller + const char *bin_name = tmp_name ? tmp_name + 1 : argv[0]; + + // Legacy executables + if (strstr(bin_name, "pedis") == bin_name) exit(pedis(argc, argv)); + if (strstr(bin_name, "peldd") == bin_name) exit(peldd(argc, argv)); + if (strstr(bin_name, "peres") == bin_name) exit(peres(argc, argv)); + if (strstr(bin_name, "pesec") == bin_name) exit(pesec(argc, argv)); + if (strstr(bin_name, "pestr") == bin_name) exit(pestr(argc, argv)); + if (strstr(bin_name, "pehash") == bin_name) exit(pehash(argc, argv)); + if (strstr(bin_name, "pepack") == bin_name) exit(pepack(argc, argv)); + if (strstr(bin_name, "pescan") == bin_name) exit(pescan(argc, argv)); + if (strstr(bin_name, "ofs2rva") == bin_name) exit(ofs2rva(argc, argv)); + if (strstr(bin_name, "rva2ofs") == bin_name) exit(rva2ofs(argc, argv)); + + static const mode_option header_mode[] = { + {"dos", NULL, 0, NULL, MODE_HEADER_DOS }, + {"coff", NULL, 0, NULL, MODE_HEADER_COFF }, + {"optional", NULL, 0, NULL, MODE_HEADER_OPTIONAL}, + {"--list", NULL, 0, NULL, 'l' }, + {"--help", NULL, 0, NULL, 1 }, + {NULL, NULL, 0, NULL, 0 } + }; + + static const mode_option resource_mode[] = { + {"--info", NULL, 0, NULL, 'i'}, + {"--statistics", NULL, 0, NULL, 's'}, + {"--extract", NULL, 0, NULL, 'e'}, + {"--name-extract", NULL, 0, NULL, 'n'}, + {"--file-version", NULL, 0, NULL, 'v'}, + {"--list", NULL, 0, NULL, 'l'}, + {"--help", NULL, 0, NULL, 1 }, + {NULL, NULL, 0, NULL, 0 } + }; + + static const mode_option directory_mode[] = { + {"--list", NULL, 0, NULL, 'l'}, + {"--help", NULL, 0, NULL, 1 }, + {NULL, NULL, 0, NULL, 0 } + }; + + static const mode_option section_mode[] = { + {"--list", NULL, 0, NULL, 'l'}, + {"--help", NULL, 0, NULL, 1 }, + {NULL, NULL, 0, NULL, 0 } + }; + + // static const mode_option hash_mode[] = { + // {"md5", NULL, 0, NULL, COMMAND_HASH_MD5 }, + // {"sha1", NULL, 0, NULL, COMMAND_HASH_SHA1 }, + // {"sha256", NULL, 0, NULL, COMMAND_HASH_SHA256 }, + // {"ssdeep", NULL, 0, NULL, COMMAND_HASH_SSDEEP }, + // {"impash", NULL, 0, NULL, COMMAND_HASH_IMPHASH}, + // {"--list", NULL, 0, NULL, 'l' }, + // {"--help", NULL, 0, NULL, 1 }, + // {NULL, NULL, 0, NULL, 0 }, + // }; + + static const mode_option base_mode[] = { + {"headers", header_mode, 0, NULL, MODE_HEADER }, + {"directories", directory_mode, 0, NULL, MODE_DIRECTORY }, + {"exports", NULL, 0, NULL, MODE_EXPORTS }, + {"imports", NULL, 0, NULL, MODE_IMPORTS }, + {"resources", NULL, 0, NULL, MODE_RESOURCES }, + {"certificates", NULL, 0, NULL, MODE_CERTIFICATES}, + {"sections", section_mode, 0, NULL, MODE_SECTION }, + {"libraries", NULL, 0, NULL, MODE_LIBRARY }, + {"strings", NULL, 0, NULL, MODE_STRINGS }, + {"security", NULL, 0, NULL, MODE_SECURITY }, + {"hash", NULL, optional_argument, NULL, COMMAND_HASH }, + {"scan", NULL, 0, NULL, COMMAND_SCAN }, + {"--format", NULL, 0, NULL, 'f' }, + {"--version", NULL, 0, NULL, 'V' }, + {"--complete", NULL, 0, NULL, 2 }, + {"--help", NULL, 0, NULL, 1 }, + {NULL, NULL, 0, NULL, 0 } + }; + + //------------------------------------------------------------------------// + /* + static const struct option common_options[] = { + {"format", required_argument, NULL, 'f'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 1 }, + {NULL, 0, NULL, 0 } + }; + + static const struct option legacy_options[] = { + {"all", no_argument, NULL, 'A'}, + {"all-headers", no_argument, NULL, 'H'}, + {"all-sections", no_argument, NULL, 'S'}, + {"header", required_argument, NULL, 'h'}, + {"imports", no_argument, NULL, 'i'}, + {"exports", no_argument, NULL, 'e'}, + {"dirs", no_argument, NULL, 'd'}, + {NULL, 0, NULL, 0 } + }; + */ + + if (argc < 2) help(); + + const mode_option *mode = base_mode; + int c, f_arg = 0, select = 0, index = 1; + + if (access(argv[index], F_OK) == 0) { + f_arg = index; + ++index; + } + + while ((c = getopt_mode(argc, argv, &mode, &select, &index))) { + if (c < 0) break; + // printf("%i\n", c); + + switch (c) { + case 1: + help(); + break; + case 2: + complete(mode); + break; + case 'V': + version(); + break; + case 'a': + argument_options.all = true; + break; + case 'l': + argument_options.list = true; + break; + + default: + break; + // exit(EXIT_FAILURE); + } + } + + if ((f_arg == 0) && (access(argv[argc - 1], F_OK) == 0)) { + f_arg = argc - 1; + } + if (f_arg == 0) help(); + + // printf("%i\n", file); + + pe_ctx_t ctx; + pe_err_e err = pe_load_file(&ctx, argv[f_arg]); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + err = pe_parse(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + if (!pe_is_pe(&ctx)) EXIT_ERROR("not a valid PE file"); + + pev_config_t config; + PEV_INITIALIZE(&config); + + output_open_document(); + + switch (select) { + case MODE_HEADER: + // TODO: --list, --all + printf("* dos\n* coff\n* optional\n"); + break; + + case MODE_HEADER_DOS: + print_dos_header(&ctx); + break; + case MODE_HEADER_COFF: + print_coff_header(&ctx); + break; + case MODE_HEADER_OPTIONAL: + print_optional_header(&ctx); + break; + + case MODE_DIRECTORY: { + IMAGE_DATA_DIRECTORY **directories = get_pe_directories(&ctx); + if (directories != NULL) print_directories(&ctx); + break; + } + + case MODE_EXPORTS: { + IMAGE_DATA_DIRECTORY **directories = get_pe_directories(&ctx); + if (directories != NULL) print_exports(&ctx); + break; + } + + case MODE_IMPORTS: { + IMAGE_DATA_DIRECTORY **directories = get_pe_directories(&ctx); + if (directories != NULL) print_imports(&ctx); + break; + } + + case MODE_RESOURCES: { + pe_resources_t *resources = pe_resources(&ctx); + if (resources == NULL || resources->err != LIBPE_E_OK) { + LIBPE_WARNING("This file has no resources"); + return EXIT_SUCCESS; + } + + pe_resource_node_t *root_node = resources->root_node; + + peres_show_nodes(&ctx, root_node); + peres_show_stats(root_node); + peres_show_list(&ctx, root_node); + // peres_save_all_resources(&ctx, root_node, options->namedExtract); + peres_show_version(&ctx, root_node); + break; + } + + // case MODE_EXCEPTIONS: + + case MODE_SECURITY: + print_securities(&ctx); + // fall through + case MODE_CERTIFICATES: { + // TODO: settings + certificate_settings set = {1, NULL}; + parse_certificates(&set, &ctx); + break; + } + + // case MODE_BASE_RELOCATIONS: + // case MODE_DEBUG: + // case MODE_ARCHITECTURE: + // case MODE_GLOBAL_PTR: + // case MODE_TLS: + // case MODE_LOAD_CONFIGS: + // case MODE_BOUND_IMPORT: + // case MODE_IAT: + // case MODE_DELAY_IMPORT_DESCRIPTOR: + // case MODE_CLR_RUNTIME_HEADER: + + case MODE_SECTION: { + if (pe_sections(&ctx) != NULL) + print_sections(&ctx); + else { + LIBPE_WARNING("unable to read sections"); + } + break; + } + + case MODE_LIBRARY: { + IMAGE_DATA_DIRECTORY **directories = pe_directories(&ctx); + if (directories == NULL) { + LIBPE_WARNING("directories not found"); + } else { + print_dependencies(&ctx); + } + + break; + } + + case MODE_STRINGS: { + string_settings set; + print_strings(&ctx, &set); + break; + } + + case COMMAND_HASH: + // case COMMAND_HASH_MD5: + // case COMMAND_HASH_SHA1: + // case COMMAND_HASH_SHA256: + // case COMMAND_HASH_SSDEEP: + // case COMMAND_HASH_IMPHASH: + // TODO + printf("Not Implemented\n"); + break; + + case COMMAND_SCAN: { + // TODO + printf("Not Implemented\n"); + break; + } + + case COMMAND_DISASSAMBLE: + case COMMAND_PACK: + case COMMAND_TRANSFORM: + // case COMMAND_ADDRESSING_RELATIVE: + // case COMMAND_ADDRESSING_OFFSET: + printf("Not Implemented\n"); + break; + default: + // printf("Unknown Argument: %d\n", select); + break; + } + + output_close_document(); + + // free + err = pe_unload(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + PEV_FINALIZE(&config); + + return EXIT_SUCCESS; +} + diff --git a/src/main.h b/src/main.h new file mode 100644 index 00000000..7afd3022 --- /dev/null +++ b/src/main.h @@ -0,0 +1,51 @@ +/* vim: set ts=4 sw=4 noet: */ +/* + readpe - the PE file analyzer toolkit + + main.h - main executable entry + + Copyright (C) 2023 readpe authors + + 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, either version 2 of the License, or + (at your option) any later version. + + 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, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + +int pedis(int argc, char *argv[]); +int pehash(int argc, char *argv[]); +int peldd(int argc, char *argv[]); +int pepack(int argc, char *argv[]); +int peres(int argc, char *argv[]); +int pescan(int argc, char *argv[]); +int pesec(int argc, char *argv[]); +int pestr(int argc, char *argv[]); + +int ofs2rva(int argc, char *argv[]); +int rva2ofs(int argc, char *argv[]); + +int _main(int argc, char *argv[]); + + diff --git a/src/ofs2rva.c b/src/ofs2rva.c index be06cddf..1729ebb3 100644 --- a/src/ofs2rva.c +++ b/src/ofs2rva.c @@ -34,9 +34,10 @@ files in the program, then also delete it here. */ +#include "main.h" +#include "common.h" // FIX: Needed if strtoull() is used and to test overflow. #include -#include "common.h" #define PROGRAM "ofs2rva" @@ -84,7 +85,7 @@ static void parse_options(int argc, char *argv[]) } } -int main(int argc, char *argv[]) +int ofs2rva(int argc, char *argv[]) { //PEV_INITIALIZE(); diff --git a/src/pedis.c b/src/pedis.c index b3e89db2..a8f5267f 100644 --- a/src/pedis.c +++ b/src/pedis.c @@ -34,11 +34,12 @@ files in the program, then also delete it here. */ +#include "main.h" #include "common.h" -#include "../lib/libudis86/udis86.h" +#include "plugins.h" #include #include -#include "plugins.h" +#include #define PROGRAM "pedis" @@ -316,7 +317,7 @@ static void disassemble_offset(pe_ctx_t *ctx, const options_t *options, ud_t *ud } } -int main(int argc, char *argv[]) +int pedis(int argc, char *argv[]) { pev_config_t config; PEV_INITIALIZE(&config); diff --git a/src/pehash.c b/src/pehash.c index f2a33f43..7f51b5a6 100644 --- a/src/pehash.c +++ b/src/pehash.c @@ -34,6 +34,7 @@ files in the program, then also delete it here. */ +#include "main.h" #include "common.h" #include "plugins.h" @@ -189,7 +190,7 @@ static void print_basic_hash(const unsigned char *data, size_t data_size) free(hash_value); } -int main(int argc, char *argv[]) +int pehash(int argc, char *argv[]) { pev_config_t config; PEV_INITIALIZE(&config); diff --git a/src/peldd.c b/src/peldd.c index 18bcc1f5..bc1fa741 100644 --- a/src/peldd.c +++ b/src/peldd.c @@ -34,9 +34,11 @@ files in the program, then also delete it here. */ +#include "peldd.h" +#include "main.h" #include "common.h" -#include #include "output.h" +#include #define PROGRAM "peldd" @@ -92,7 +94,7 @@ static void parse_options(int argc, char *argv[]) } } -static void print_dependencies(pe_ctx_t *ctx) +void print_dependencies(pe_ctx_t *ctx) { output_open_scope("Dependencies", OUTPUT_SCOPE_TYPE_ARRAY); const pe_imports_t *imports = pe_imports(ctx); @@ -103,7 +105,7 @@ static void print_dependencies(pe_ctx_t *ctx) output_close_scope(); } -int main(int argc, char *argv[]) +int peldd(int argc, char *argv[]) { pev_config_t config; PEV_INITIALIZE(&config); diff --git a/src/pepack.c b/src/pepack.c index cacf5478..b7f9794c 100644 --- a/src/pepack.c +++ b/src/pepack.c @@ -34,6 +34,7 @@ files in the program, then also delete it here. */ +#include "main.h" #include "common.h" #include "plugins.h" @@ -241,7 +242,7 @@ static bool compare_signature(const unsigned char *data, uint64_t ep_offset, FIL return false; } -int main(int argc, char *argv[]) +int pepack(int argc, char *argv[]) { pev_config_t config; PEV_INITIALIZE(&config); diff --git a/src/peres.c b/src/peres.c index b766835d..2f8c375b 100644 --- a/src/peres.c +++ b/src/peres.c @@ -34,6 +34,8 @@ files in the program, then also delete it here. */ +#include "peres.h" +#include "main.h" #include "common.h" #include #include @@ -256,7 +258,7 @@ static void peres_show_node(pe_ctx_t *ctx, const pe_resource_node_t *node) } } -static void peres_show_nodes(pe_ctx_t *ctx, const pe_resource_node_t *node) +void peres_show_nodes(pe_ctx_t *ctx, const pe_resource_node_t *node) { if (node == NULL) return; @@ -303,7 +305,7 @@ static void peres_show_list_node(pe_ctx_t *ctx, const pe_resource_node_t *node) printf("%s (%d bytes)\n", node_info, node->raw.dataEntry->Size); } -static void peres_show_list(pe_ctx_t *ctx, const pe_resource_node_t *node) +void peres_show_list(pe_ctx_t *ctx, const pe_resource_node_t *node) { if (node == NULL) return; @@ -527,7 +529,7 @@ static void peres_save_resource(pe_ctx_t *ctx, const pe_resource_node_t *node, b free( relativeFileName ); } -static void peres_save_all_resources(pe_ctx_t *ctx, const pe_resource_node_t *node, bool namedExtract) +void peres_save_all_resources(pe_ctx_t *ctx, const pe_resource_node_t *node, bool namedExtract) { if (node == NULL) return; @@ -548,7 +550,7 @@ bool peres_contains_version_node(const pe_resource_node_t *node) { return node->raw.directoryEntry->u0.data.NameOffset == RT_VERSION; } -static void peres_show_version(pe_ctx_t *ctx, const pe_resource_node_t *node) +void peres_show_version(pe_ctx_t *ctx, const pe_resource_node_t *node) { if (node == NULL) return; @@ -628,7 +630,7 @@ static void peres_generate_stats(peres_stats_t *stats, const pe_resource_node_t } } -static void peres_show_stats(const pe_resource_node_t *node) +void peres_show_stats(const pe_resource_node_t *node) { peres_stats_t stats = {0}; peres_generate_stats(&stats, node); @@ -651,7 +653,7 @@ static void peres_show_stats(const pe_resource_node_t *node) output("Total Data Entry", value); } -int main(int argc, char **argv) +int peres(int argc, char **argv) { pev_config_t config; PEV_INITIALIZE(&config); diff --git a/src/pescan.c b/src/pescan.c index c8e9d19b..7d0578b7 100644 --- a/src/pescan.c +++ b/src/pescan.c @@ -34,11 +34,12 @@ files in the program, then also delete it here. */ +#include "main.h" #include "common.h" +#include "plugins.h" #include #include #include -#include "plugins.h" #define PROGRAM "pescan" @@ -453,7 +454,7 @@ static int8_t cpl_analysis(pe_ctx_t *ctx) return 0; } -int main(int argc, char *argv[]) +int pescan(int argc, char *argv[]) { pev_config_t config; PEV_INITIALIZE(&config); diff --git a/src/pesec.c b/src/pesec.c index 25915fd9..ce3d9b7c 100644 --- a/src/pesec.c +++ b/src/pesec.c @@ -34,26 +34,22 @@ files in the program, then also delete it here. */ +#include "pesec.h" +#include "libpe/context.h" +#include "libpe/pe.h" +#include "main.h" #include "common.h" +#include "compat/strlcat.h" +#include "plugins.h" #include #include #include #include -#include "compat/strlcat.h" -#include "plugins.h" #define PROGRAM "pesec" -typedef enum { - CERT_FORMAT_TEXT = 1, - CERT_FORMAT_PEM = 2, - CERT_FORMAT_DER = 3 -} cert_format_e; - -typedef struct { - cert_format_e certoutform; - BIO *certout; -} options_t; +// FIXME +typedef certificate_settings options_t; static void usage(void) { @@ -175,7 +171,7 @@ find stack cookies, a.k.a canary, buffer security check option in MVS 2010 */ // FIXME: What about other versions? -static bool stack_cookies(pe_ctx_t *ctx) +bool stack_cookies(pe_ctx_t *ctx) { static const unsigned char mvs2010[] = { 0x55, 0x8b, 0xec, 0x83, @@ -203,6 +199,44 @@ static bool stack_cookies(pe_ctx_t *ctx) return found == sizeof(mvs2010); } +void print_securities(pe_ctx_t *ctx) +{ + IMAGE_OPTIONAL_HEADER *optional = pe_optional(ctx); + if (optional == NULL) exit(EXIT_FAILURE); // FIXME: exit + + uint16_t dllchar = 0; + switch (optional->type) { + default: + exit(EXIT_FAILURE); // FIXME: exit + case MAGIC_PE32: + dllchar = optional->_32->DllCharacteristics; + break; + case MAGIC_PE64: + dllchar = optional->_64->DllCharacteristics; + break; + } + static char field[MAX_MSG]; + // aslr + snprintf(field, MAX_MSG, "ASLR"); + output(field, (dllchar & 0x40) ? "yes" : "no"); + + // dep/nx + snprintf(field, MAX_MSG, "DEP/NX"); + output(field, (dllchar & 0x100) ? "yes" : "no"); + + // seh + snprintf(field, MAX_MSG, "SEH"); + output(field, (dllchar & 0x400) ? "no" : "yes"); + + // cfg + snprintf(field, MAX_MSG, "CFG"); + output(field, (dllchar & 0x4000) ? "yes" : "no"); + + // stack cookies + snprintf(field, MAX_MSG, "Stack cookies (EXPERIMENTAL)"); + output(field, stack_cookies(ctx) ? "yes" : "no"); +} + static void print_certificate(BIO *out, cert_format_e format, X509 *cert) { if (out == NULL) @@ -329,7 +363,7 @@ static int parse_pkcs7_data(const options_t *options, const CRYPT_DATA_BLOB *blo return result; } -static void parse_certificates(const options_t *options, pe_ctx_t *ctx) +void parse_certificates(const options_t *options, pe_ctx_t *ctx) { const IMAGE_DATA_DIRECTORY * const directory = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_SECURITY); if (directory == NULL) @@ -432,7 +466,7 @@ static void parse_certificates(const options_t *options, pe_ctx_t *ctx) output_close_scope(); // certificates } -int main(int argc, char *argv[]) +int pesec(int argc, char *argv[]) { pev_config_t config; PEV_INITIALIZE(&config); @@ -464,45 +498,7 @@ int main(int argc, char *argv[]) if (!pe_is_pe(&ctx)) EXIT_ERROR("not a valid PE file"); - IMAGE_OPTIONAL_HEADER *optional = pe_optional(&ctx); - if (optional == NULL) - return EXIT_FAILURE; - - uint16_t dllchar = 0; - switch (optional->type) { - default: - return EXIT_FAILURE; - case MAGIC_PE32: - dllchar = optional->_32->DllCharacteristics; - break; - case MAGIC_PE64: - dllchar = optional->_64->DllCharacteristics; - break; - } - - output_open_document(); - - static char field[MAX_MSG]; - - // aslr - snprintf(field, MAX_MSG, "ASLR"); - output(field, (dllchar & 0x40) ? "yes" : "no"); - - // dep/nx - snprintf(field, MAX_MSG, "DEP/NX"); - output(field, (dllchar & 0x100) ? "yes" : "no"); - - // seh - snprintf(field, MAX_MSG, "SEH"); - output(field, (dllchar & 0x400) ? "no" : "yes"); - - // cfg - snprintf(field, MAX_MSG, "CFG"); - output(field, (dllchar & 0x4000) ? "yes" : "no"); - - // stack cookies - snprintf(field, MAX_MSG, "Stack cookies (EXPERIMENTAL)"); - output(field, stack_cookies(&ctx) ? "yes" : "no"); + print_securities(&ctx); // certificados parse_certificates(options, &ctx); diff --git a/src/pestr.c b/src/pestr.c index 62efbfaf..26b6119c 100644 --- a/src/pestr.c +++ b/src/pestr.c @@ -34,6 +34,8 @@ files in the program, then also delete it here. */ +#include "pestr.h" +#include "main.h" #include "common.h" #include #include @@ -44,11 +46,8 @@ #define BUFSIZE 4 #define LINE_BUFFER 32768 -typedef struct { - unsigned short strsize; - bool offset; - bool section; -} options_t; +// FIXME +typedef string_settings options_t; static void usage(void) { @@ -181,35 +180,9 @@ static void printb( pe_ctx_t *ctx, putchar('\n'); } -int main(int argc, char *argv[]) -{ - if (argc < 2) { - usage(); - exit(EXIT_FAILURE); - } - - options_t *options = parse_options(argc, argv); // opcoes - - const char *path = argv[argc-1]; - pe_ctx_t ctx; - - pe_err_e err = pe_load_file(&ctx, path); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - err = pe_parse(&ctx); - if (err != LIBPE_E_OK) { - pe_error_print(stderr, err); - return EXIT_FAILURE; - } - - if (!pe_is_pe(&ctx)) - EXIT_ERROR("not a valid PE file"); - - const uint64_t pe_size = pe_filesize(&ctx); - const uint8_t *pe_raw_data = ctx.map_addr; +void print_strings( pe_ctx_t *ctx, const options_t *options) { + const uint64_t pe_size = pe_filesize(ctx); + const uint8_t *pe_raw_data = ctx->map_addr; uint16_t chunk; size_t buff_start = 0; @@ -232,7 +205,7 @@ int main(int argc, char *argv[]) } else { if ( buff_start != 0 ) { if ((pe_raw_offset - buff_start) >= (options->strsize ? options->strsize : 4)) - printb(&ctx, options, pe_raw_data, buff_start, pe_raw_offset, false); + printb(ctx, options, pe_raw_data, buff_start, pe_raw_offset, false); buff_start = 0; } } @@ -251,18 +224,49 @@ int main(int argc, char *argv[]) if( pe_raw_offset & 0x1 ) { if ( odd_wbuff_start != 0 ) { if ((pe_raw_offset - odd_wbuff_start)/2 >= (options->strsize ? options->strsize : 4)) - printb(&ctx, options, pe_raw_data, odd_wbuff_start, pe_raw_offset, true); + printb(ctx, options, pe_raw_data, odd_wbuff_start, pe_raw_offset, true); odd_wbuff_start = 0; } } else { if ( even_wbuff_start != 0 ) { if ((pe_raw_offset - even_wbuff_start)/2 >= (options->strsize ? options->strsize : 4)) - printb(&ctx, options, pe_raw_data, even_wbuff_start, pe_raw_offset, true); + printb(ctx, options, pe_raw_data, even_wbuff_start, pe_raw_offset, true); even_wbuff_start = 0; } } } } +} + +int pestr(int argc, char *argv[]) +{ + if (argc < 2) { + usage(); + exit(EXIT_FAILURE); + } + + options_t *options = parse_options(argc, argv); // opcoes + + const char *path = argv[argc-1]; + pe_ctx_t ctx; + + pe_err_e err = pe_load_file(&ctx, path); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + err = pe_parse(&ctx); + if (err != LIBPE_E_OK) { + pe_error_print(stderr, err); + return EXIT_FAILURE; + } + + if (!pe_is_pe(&ctx)) + EXIT_ERROR("not a valid PE file"); + + print_strings(&ctx, options); + // libera a memoria free_options(options); diff --git a/src/readpe.c b/src/readpe.c index 74ca94f9..6d8cbf7e 100644 --- a/src/readpe.c +++ b/src/readpe.c @@ -34,10 +34,14 @@ files in the program, then also delete it here. */ +#include "readpe.h" +#include "libpe/context.h" +#include "libpe/directories.h" +#include "main.h" #include "common.h" +#include "output.h" #include #include -#include "output.h" #define PROGRAM "readpe" @@ -74,6 +78,15 @@ static void usage(void) PROGRAM, PROGRAM, formats); } +IMAGE_DATA_DIRECTORY **get_pe_directories(pe_ctx_t *ctx) +{ + IMAGE_DATA_DIRECTORY **directories = pe_directories(ctx); + if (directories == NULL) + LIBPE_WARNING("directories not found"); + + return directories; +} + static void parse_headers(options_t *options, const char *optarg) { if (!strcmp(optarg, "dos")) @@ -173,7 +186,7 @@ static options_t *parse_options(int argc, char *argv[]) return options; } -static void print_sections(pe_ctx_t *ctx) +void print_sections(pe_ctx_t *ctx) { #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 static const char * const flags_name[] = { @@ -276,7 +289,7 @@ static void print_sections(pe_ctx_t *ctx) output_close_scope(); // Sections } -static void print_directories(pe_ctx_t *ctx) +void print_directories(pe_ctx_t *ctx) { #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 typedef struct { @@ -333,8 +346,12 @@ static void print_directories(pe_ctx_t *ctx) output_close_scope(); // Data directories } -static void print_optional_header(IMAGE_OPTIONAL_HEADER *header) +void print_optional_header(pe_ctx_t *ctx) { + IMAGE_OPTIONAL_HEADER *header = pe_optional(ctx); + if (!header) + LIBPE_WARNING("unable to read Optional (Image) file header"); + #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 typedef struct { WindowsSubsystem subsystem; @@ -599,8 +616,12 @@ static void print_optional_header(IMAGE_OPTIONAL_HEADER *header) output_close_scope(); // Optional/Image heade } -static void print_coff_header(IMAGE_COFF_HEADER *header) +void print_coff_header(pe_ctx_t *ctx) { + IMAGE_COFF_HEADER *header = pe_coff(ctx); + if (!header) + LIBPE_WARNING("unable to read COFF file header"); + #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 typedef struct { ImageCharacteristics characteristic; @@ -721,8 +742,13 @@ static void print_coff_header(IMAGE_COFF_HEADER *header) output_close_scope(); // COFF/File header } -static void print_dos_header(IMAGE_DOS_HEADER *header) +void print_dos_header(pe_ctx_t *ctx) { + + IMAGE_DOS_HEADER *header = pe_dos(ctx); + if (!header) + LIBPE_WARNING("unable to read DOS header"); + char s[MAX_MSG]; output_open_scope("DOS Header", OUTPUT_SCOPE_TYPE_OBJECT); @@ -778,7 +804,7 @@ static void print_dos_header(IMAGE_DOS_HEADER *header) output_close_scope(); // DOS Header } -static void print_exports(pe_ctx_t *ctx) +void print_exports(pe_ctx_t *ctx) { output_open_scope("Exported functions", OUTPUT_SCOPE_TYPE_ARRAY); @@ -824,7 +850,7 @@ static void print_exports(pe_ctx_t *ctx) output_close_scope(); // Exported functions } -static void print_imports(pe_ctx_t *ctx) +void print_imports(pe_ctx_t *ctx) { output_open_scope("Imported functions", OUTPUT_SCOPE_TYPE_ARRAY); @@ -860,7 +886,7 @@ static void print_imports(pe_ctx_t *ctx) output_close_scope(); // Imported functions } -int main(int argc, char *argv[]) +int _main(int argc, char *argv[]) { pev_config_t config; PEV_INITIALIZE(&config); @@ -895,59 +921,37 @@ int main(int argc, char *argv[]) // dos header if (options->dos || options->all_headers || options->all) { - IMAGE_DOS_HEADER *header_ptr = pe_dos(&ctx); - if (header_ptr) - print_dos_header(header_ptr); - else { LIBPE_WARNING("unable to read DOS header"); } + print_dos_header(&ctx); } // coff/file header if (options->coff || options->all_headers || options->all) { - IMAGE_COFF_HEADER *header_ptr = pe_coff(&ctx); - if (header_ptr) - print_coff_header(header_ptr); - else { LIBPE_WARNING("unable to read COFF file header"); } + print_coff_header(&ctx); } // optional header if (options->opt || options->all_headers || options->all) { - IMAGE_OPTIONAL_HEADER *header_ptr = pe_optional(&ctx); - if (header_ptr) - print_optional_header(header_ptr); - else { LIBPE_WARNING("unable to read Optional (Image) file header"); } + print_optional_header(&ctx); } - IMAGE_DATA_DIRECTORY **directories = pe_directories(&ctx); - bool directories_warned = false; + IMAGE_DATA_DIRECTORY **directories = get_pe_directories(&ctx); // directories if (options->dirs || options->all) { if (directories != NULL) print_directories(&ctx); - else if (!directories_warned) { - LIBPE_WARNING("directories not found"); - directories_warned = true; - } } // imports if (options->imports || options->all) { if (directories != NULL) print_imports(&ctx); - else if (!directories_warned) { - LIBPE_WARNING("directories not found"); - directories_warned = true; - } } // exports if (options->exports || options->all) { if (directories != NULL) print_exports(&ctx); - else if (!directories_warned) { - LIBPE_WARNING("directories not found"); - directories_warned = true; - } } // sections diff --git a/src/rva2ofs.c b/src/rva2ofs.c index 5374d07c..8d73f8fb 100644 --- a/src/rva2ofs.c +++ b/src/rva2ofs.c @@ -34,6 +34,7 @@ files in the program, then also delete it here. */ +#include "main.h" #include "common.h" #define PROGRAM "rva2ofs" @@ -82,7 +83,7 @@ static void parse_options(int argc, char *argv[]) } } -int main(int argc, char *argv[]) +int rva2ofs(int argc, char *argv[]) { //PEV_INITIALIZE(); From 476527ee28ff81b5dc72e566c110e2b36e9562f1 Mon Sep 17 00:00:00 2001 From: Ophelia Beatrice de Sica Date: Sun, 23 Jul 2023 05:29:22 +0200 Subject: [PATCH 2/4] Update main.c --- .gitignore | 1 + include/common.h | 31 +++++----- src/main.c | 149 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 136 insertions(+), 45 deletions(-) diff --git a/.gitignore b/.gitignore index 3a0d9dc8..764a209d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ Debug *.dSYM *.log .settings +build/** src/ofs2rva src/output src/pedis diff --git a/include/common.h b/include/common.h index d50d83c2..b60a6223 100644 --- a/include/common.h +++ b/include/common.h @@ -73,19 +73,18 @@ void *malloc_s(size_t size); void *calloc_s(size_t nmemb, size_t size); -#define PEV_INITIALIZE(config) \ - do { \ - memset(config, 0, sizeof(*config)); \ - pev_load_config(config); \ - int ret = plugins_load_all(config); \ - if (ret < 0) \ - exit(EXIT_FAILURE); \ - output_init(); /* Requires plugin for text output. */ \ - } while (0) - -#define PEV_FINALIZE(config) \ - do { \ - output_term(); \ - plugins_unload_all(); \ - pev_cleanup_config(config); \ - } while (0) +static inline void PEV_INITIALIZE(pev_config_t *config) { + memset(config, 0, sizeof(*config)); + pev_load_config(config); + int ret = plugins_load_all(config); + if (ret < 0) + exit(EXIT_FAILURE); + output_init(); /* Requires plugin for text output. */ +} + +static inline void PEV_FINALIZE(pev_config_t *config) { + output_term(); + plugins_unload_all(); + pev_cleanup_config(config); +} + diff --git a/src/main.c b/src/main.c index a8f6b73d..f34b9223 100644 --- a/src/main.c +++ b/src/main.c @@ -86,6 +86,7 @@ enum ARGUMENTS { MODE_END, COMMAND_SCAN = 2000, // -- pescan + COMMAND_EXTRACT, COMMAND_HASH = 2100, // -- pehash // COMMAND_HASH_MD5, // COMMAND_HASH_SHA1, @@ -112,6 +113,7 @@ enum HASH_ALGO { static struct argument_options_t { unsigned int list : 1; unsigned int all : 1; + char *format; } argument_options; typedef struct { @@ -200,6 +202,90 @@ int main(int argc, char *argv[]) { if (strstr(bin_name, "ofs2rva") == bin_name) exit(ofs2rva(argc, argv)); if (strstr(bin_name, "rva2ofs") == bin_name) exit(rva2ofs(argc, argv)); + //------------------------------------------------------------------------// + + static const struct option readpe_options[] = { + {"help", no_argument, NULL, 1 }, + {"all", no_argument, NULL, 'A'}, + {"all-headers", no_argument, NULL, 'H'}, + {"all-sections", no_argument, NULL, 'S'}, + {"header", required_argument, NULL, 'h'}, + {"imports", no_argument, NULL, 'i'}, + {"exports", no_argument, NULL, 'e'}, + {"dirs", no_argument, NULL, 'd'}, + {"format", required_argument, NULL, 'f'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + static const struct option pedis_options[] = { + {"help", no_argument, NULL, 1 }, + {"att", no_argument, NULL, 2 }, + {"", required_argument, NULL, 'n'}, + {"entrypoint", no_argument, NULL, 'e'}, + {"mode", required_argument, NULL, 'm'}, + {"offset", required_argument, NULL, 'o'}, + {"rva", required_argument, NULL, 'r'}, + {"section", required_argument, NULL, 's'}, + {"format", required_argument, NULL, 'f'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + static const struct option pehash_options[] = { + {"help", no_argument, NULL, 1 }, + {"format", required_argument, NULL, 'f'}, + {"all", no_argument, NULL, 'a'}, + {"content", no_argument, NULL, 'c'}, + {"header", required_argument, NULL, 'h'}, + {"section-name", required_argument, NULL, 's'}, + {"section-index", required_argument, NULL, 2 }, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + static const struct option peres_options[] = { + {"all", required_argument, NULL, 'a'}, + {"format", required_argument, NULL, 'f'}, + {"info", no_argument, NULL, 'i'}, + {"list", no_argument, NULL, 'l'}, + {"statistics", no_argument, NULL, 's'}, + {"extract", no_argument, NULL, 'x'}, + {"named-extract", no_argument, NULL, 'X'}, + {"file-version", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 1 }, + {NULL, 0, NULL, 0 } + }; + + static const struct option pescan_options[] = { + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 1 }, + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + static const struct option pesec_options[] = { + {"format", required_argument, NULL, 'f'}, + {"certoutform", required_argument, NULL, 'c'}, + {"certout", required_argument, NULL, 'o'}, + {"help", no_argument, NULL, 1 }, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + static const struct option pestr_options[] = { + {"offset", no_argument, NULL, 'o'}, + {"section", no_argument, NULL, 's'}, + {"min-length", required_argument, NULL, 'n'}, + {"help", no_argument, NULL, 1 }, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0 } + }; + + //------------------------------------------------------------------------// + static const mode_option header_mode[] = { {"dos", NULL, 0, NULL, MODE_HEADER_DOS }, {"coff", NULL, 0, NULL, MODE_HEADER_COFF }, @@ -210,14 +296,14 @@ int main(int argc, char *argv[]) { }; static const mode_option resource_mode[] = { - {"--info", NULL, 0, NULL, 'i'}, - {"--statistics", NULL, 0, NULL, 's'}, - {"--extract", NULL, 0, NULL, 'e'}, - {"--name-extract", NULL, 0, NULL, 'n'}, - {"--file-version", NULL, 0, NULL, 'v'}, - {"--list", NULL, 0, NULL, 'l'}, - {"--help", NULL, 0, NULL, 1 }, - {NULL, NULL, 0, NULL, 0 } + {"extract", NULL, 0, NULL, COMMAND_EXTRACT}, + {"--info", NULL, 0, NULL, 'i' }, + {"--statistics", NULL, 0, NULL, 's' }, + {"--name-extract", NULL, 0, NULL, 'n' }, + {"--file-version", NULL, 0, NULL, 'v' }, + {"--list", NULL, 0, NULL, 'l' }, + {"--help", NULL, 0, NULL, 1 }, + {NULL, NULL, 0, NULL, 0 } }; static const mode_option directory_mode[] = { @@ -244,23 +330,23 @@ int main(int argc, char *argv[]) { // }; static const mode_option base_mode[] = { - {"headers", header_mode, 0, NULL, MODE_HEADER }, - {"directories", directory_mode, 0, NULL, MODE_DIRECTORY }, - {"exports", NULL, 0, NULL, MODE_EXPORTS }, - {"imports", NULL, 0, NULL, MODE_IMPORTS }, - {"resources", NULL, 0, NULL, MODE_RESOURCES }, - {"certificates", NULL, 0, NULL, MODE_CERTIFICATES}, - {"sections", section_mode, 0, NULL, MODE_SECTION }, - {"libraries", NULL, 0, NULL, MODE_LIBRARY }, - {"strings", NULL, 0, NULL, MODE_STRINGS }, - {"security", NULL, 0, NULL, MODE_SECURITY }, - {"hash", NULL, optional_argument, NULL, COMMAND_HASH }, - {"scan", NULL, 0, NULL, COMMAND_SCAN }, - {"--format", NULL, 0, NULL, 'f' }, - {"--version", NULL, 0, NULL, 'V' }, - {"--complete", NULL, 0, NULL, 2 }, - {"--help", NULL, 0, NULL, 1 }, - {NULL, NULL, 0, NULL, 0 } + {"headers", header_mode, 0, NULL, MODE_HEADER }, + {"directories", directory_mode, 0, NULL, MODE_DIRECTORY }, + {"exports", NULL, 0, NULL, MODE_EXPORTS }, + {"imports", NULL, 0, NULL, MODE_IMPORTS }, + {"resources", NULL, 0, NULL, MODE_RESOURCES }, + {"certificates", NULL, 0, NULL, MODE_CERTIFICATES}, + {"sections", section_mode, 0, NULL, MODE_SECTION }, + {"libraries", NULL, 0, NULL, MODE_LIBRARY }, + {"strings", NULL, 0, NULL, MODE_STRINGS }, + {"security", NULL, 0, NULL, MODE_SECURITY }, + {"hash", NULL, 0, NULL, COMMAND_HASH }, + {"scan", NULL, 0, NULL, COMMAND_SCAN }, + {"--format", NULL, 0, NULL, 'f' }, + {"--version", NULL, 0, NULL, 'V' }, + {"--complete", NULL, 0, NULL, 2 }, + {"--help", NULL, 0, NULL, 1 }, + {NULL, NULL, 0, NULL, 0 } }; //------------------------------------------------------------------------// @@ -311,6 +397,9 @@ int main(int argc, char *argv[]) { case 'a': argument_options.all = true; break; + case 'f': + argument_options.format = "text"; + break; case 'l': argument_options.list = true; break; @@ -349,11 +438,13 @@ int main(int argc, char *argv[]) { output_open_document(); switch (select) { + case 0: case MODE_HEADER: // TODO: --list, --all - printf("* dos\n* coff\n* optional\n"); + print_dos_header(&ctx); + print_coff_header(&ctx); + print_optional_header(&ctx); break; - case MODE_HEADER_DOS: print_dos_header(&ctx); break; @@ -402,10 +493,10 @@ int main(int argc, char *argv[]) { // case MODE_EXCEPTIONS: case MODE_SECURITY: - print_securities(&ctx); + print_securities(&ctx); // fall through case MODE_CERTIFICATES: { - // TODO: settings + // TODO: settings certificate_settings set = {1, NULL}; parse_certificates(&set, &ctx); break; From 7f6bfb411c401ed0363dad7cb6f8af8ee661eedb Mon Sep 17 00:00:00 2001 From: Ophelia Beatrice de Sica Date: Tue, 25 Jul 2023 13:15:57 +0200 Subject: [PATCH 3/4] Move text output plugin into main executable --- include/common.h | 4 +-- src/output.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/include/common.h b/include/common.h index b60a6223..49515a2b 100644 --- a/include/common.h +++ b/include/common.h @@ -76,9 +76,7 @@ void *calloc_s(size_t nmemb, size_t size); static inline void PEV_INITIALIZE(pev_config_t *config) { memset(config, 0, sizeof(*config)); pev_load_config(config); - int ret = plugins_load_all(config); - if (ret < 0) - exit(EXIT_FAILURE); + plugins_load_all(config); output_init(); /* Requires plugin for text output. */ } diff --git a/src/output.c b/src/output.c index b7a2f130..860d0baf 100644 --- a/src/output.c +++ b/src/output.c @@ -47,7 +47,7 @@ // Global variables // -#define FORMAT_ID_FOR_TEXT 3 +const unsigned int TEXT_SPACES = 32; static bool g_is_document_open = false; static const format_t *g_format = NULL; @@ -63,6 +63,87 @@ typedef struct _format_entry { static SLIST_HEAD(_format_t_list, _format_entry) g_registered_formats = SLIST_HEAD_INITIALIZER(g_registered_formats); +// +// Internal text output +// + +static char *escape_text(const format_t *format, const char *str) { + return output_plugin_api_ptr()->escape(format, str); +} + +static void to_text_format( + const format_t *format, + const output_type_e type, + const output_scope_t *scope, + const char *key, + const char *value) +{ + static int indent = 0; + + char * const escaped_key = format->escape_fn(format, key); + char * const escaped_value = format->escape_fn(format, value); + + switch (type) { + default: + break; + case OUTPUT_TYPE_SCOPE_OPEN: + switch (scope->type) { + default: + break; + case OUTPUT_SCOPE_TYPE_DOCUMENT: + break; + case OUTPUT_SCOPE_TYPE_OBJECT: + if (key) { + printf(INDENT(indent++, "%s\n"), escaped_key); + } else { + indent++; + } + break; + case OUTPUT_SCOPE_TYPE_ARRAY: + //putchar('\n'); + if (key) { + printf(INDENT(indent++, "%s\n"), escaped_key); + } else { + indent++; + } + break; + } + break; + case OUTPUT_TYPE_SCOPE_CLOSE: + indent--; + break; + case OUTPUT_TYPE_ATTRIBUTE: + { + const size_t key_size = key ? strlen(key) : 0; + if (key && value) { + printf(INDENT(indent, "%s:%*c%s\n"), escaped_key, (int)(TEXT_SPACES - key_size), ' ', escaped_value); + } else if (key) { + printf(INDENT(indent, "%s\n"), escaped_key); + } else if (value) { + printf(INDENT(indent, "%*c%s\n"), (int)(TEXT_SPACES - key_size + 1), ' ', escaped_value); + } + break; + } + } + + if (escaped_key != NULL) + free(escaped_key); + if (escaped_value != NULL) + free(escaped_value); +} + +// ---------------------------------------------------------------------------- + +static const format_t g_text_format = { + 0, + "text", + &to_text_format, + &escape_text, + NULL +}; + + + // // Definition of internal functions // @@ -124,7 +205,7 @@ void output(const char *key, const char *value) { } void output_init(void) { - g_format = _lookup_format_by_id(FORMAT_ID_FOR_TEXT); + g_format = &g_text_format; g_scope_stack = STACK_ALLOC(15); if (g_scope_stack == NULL) abort(); From bc54b0b5d9dd5816dd73c0f34597e9699bcaf3c8 Mon Sep 17 00:00:00 2001 From: Ophelia Beatrice de Sica Date: Mon, 16 Oct 2023 11:07:25 +0200 Subject: [PATCH 4/4] Small changes --- lib/libpe/include/libpe/pe.h | 6 +- src/main.c | 330 +++++++++++++++++++++++------------ src/output.c | 8 +- src/peres.c | 2 +- src/readpe.c | 9 +- 5 files changed, 235 insertions(+), 120 deletions(-) diff --git a/lib/libpe/include/libpe/pe.h b/lib/libpe/include/libpe/pe.h index f7820071..bdaae56d 100644 --- a/lib/libpe/include/libpe/pe.h +++ b/lib/libpe/include/libpe/pe.h @@ -22,6 +22,7 @@ #ifndef LIBPE_H #define LIBPE_H +#include #ifdef __cplusplus extern "C" { #endif @@ -46,7 +47,10 @@ extern "C" { #include "resources.h" #include "utils.h" -#define MAGIC_MZ 0x5a4d // Belongs to the DOS header +static const char __MAGIC_MZ[2] = {'M','Z'}; +static const uint16_t MAGIC_MZ = ('Z' <<8) + 'M'; + +// #define MAGIC_MZ 0x5a4d // Belongs to the DOS header #define MAX_DIRECTORIES 16 #define MAX_SECTIONS 96 diff --git a/src/main.c b/src/main.c index f34b9223..2a648e8a 100644 --- a/src/main.c +++ b/src/main.c @@ -43,6 +43,7 @@ #include "pestr.h" #include "readpe.h" #include +#include #include #include #include @@ -77,7 +78,7 @@ enum ARGUMENTS { // MODE_IAT, // MODE_DELAY_IMPORT_DESCRIPTOR, // MODE_CLR_RUNTIME_HEADER, - MODE_SECURITY, // TODO: Duplicate of MODE_CERTIFICATES, + MODE_SECURITY, // Duplicate of MODE_CERTIFICATES, MODE_SECTION = 1200, MODE_LIBRARY = 1300, // -- peldd MODE_STRINGS = 1400, // -- pestr @@ -96,7 +97,6 @@ enum ARGUMENTS { COMMAND_DISASSAMBLE = 100000, // -- pedis COMMAND_PACK, // -- pepack - COMMAND_TRANSFORM = 101000, // COMMAND_ADDRESSING_RELATIVE, // -- ofs2rva // COMMAND_ADDRESSING_OFFSET, // -- rva2ofs }; @@ -110,23 +110,54 @@ enum HASH_ALGO { HASH_ALL }; -static struct argument_options_t { +enum ARG_VARIABLE_TYPE { + VAR_BOOL = 1, + VAR_CSTRING, + VAR_UINT16 +}; + +static struct _conf_t { unsigned int list : 1; unsigned int all : 1; - char *format; -} argument_options; + char *format; + certificate_settings certificate; + string_settings string; +} conf; typedef struct { const char *const name; const void *const sub; + const int value; const int has_arg; - const int *const flag; - const int val; + const void *variable; + const enum ARG_VARIABLE_TYPE type; } mode_option; -int getopt_mode(int argc, char *const argv[], const mode_option **mode_options, - int *restrict select, int *restrict index) { - if (*index >= argc) return -1; +bool str2bool(const char *const str) +{ + if (str == NULL) + { + // TODO Do this better + EXIT_ERROR("Internal error (str2bool)"); + } + else if (*str == '0') + return false; + else if (*str == '1') + return true; + else if (strcmp(str, "false") == 0) + return false; + else if (strcmp(str, "true") == 0) + return true; + else + EXIT_ERROR("Bad bool variable"); +} + +int getopt_mode(int argc, char *const argv[], + const mode_option **const mode_options, int *restrict select, + int *restrict index) +{ + if (*index >= argc) + return -1; char *const arg = argv[*index]; *index += 1; @@ -134,15 +165,52 @@ int getopt_mode(int argc, char *const argv[], const mode_option **mode_options, for (int i = 0; (*mode_options)[i].name != NULL; ++i) { mode_option opt = (*mode_options)[i]; + char *eq = strchr(arg, '='); + + if(eq != NULL) { + *eq = '\0'; + } // printf("%s %s\n", arg, opt.name); - if (strcmp(arg, opt.name) == 0) { - val = opt.val; + if (strcmp(opt.name, arg) == 0) { + val = opt.value; + + if (opt.variable != NULL) { + char *var; + if (opt.has_arg) { + if (eq == NULL) { + if (*index < argc) { + var = argv[*index]; + *index += 1; + } else { + EXIT_ERROR("NO NEXT VAL ERROR"); + } + } else { + var = eq+1; + } + } + + switch (opt.type) { + case VAR_BOOL: + if (opt.has_arg) + *(bool *)opt.variable = str2bool(var); + else + *(bool *)opt.variable = true; + break; + case VAR_UINT16: + if (!opt.has_arg) + *(uint16_t *)opt.variable = 99; + break; + } + } + if (opt.sub != NULL) { *mode_options = opt.sub; } + if (val >= MODE_START) { *select = val; } + break; } } @@ -151,7 +219,8 @@ int getopt_mode(int argc, char *const argv[], const mode_option **mode_options, } // GNU compliant version output -void version(void) { +__attribute__((noreturn)) void version(void) +{ printf("readpe %s\n" "Copyright (C) 2023 readpe authors\n" "License GPLv2+: GNU GPL version 2 or later " @@ -164,18 +233,22 @@ void version(void) { } // GNU compliant help output -void help(void) { +__attribute__((noreturn)) void help(void) +{ printf("Usage: readpe [] [] [] []\n"); - printf("\nReport bugs to: https://github.com/mentebinaria/readpe/issues\n"); + printf( + "\nReport bugs to: https://github.com/mentebinaria/readpe/issues\n"); exit(EXIT_SUCCESS); } -void complete(const mode_option *opts) { +void complete(const mode_option *opts) +{ size_t i = 0; const mode_option *c; while ((c = &opts[i])) { - if (c->name == NULL) break; + if (c->name == NULL) + break; printf("%s ", c->name); ++i; } @@ -185,22 +258,33 @@ void complete(const mode_option *opts) { void usage(void) {} -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ const char *tmp_name = strrchr(argv[0], '/'); // If no '/' in caller (called from env) we set it to the original caller const char *bin_name = tmp_name ? tmp_name + 1 : argv[0]; // Legacy executables - if (strstr(bin_name, "pedis") == bin_name) exit(pedis(argc, argv)); - if (strstr(bin_name, "peldd") == bin_name) exit(peldd(argc, argv)); - if (strstr(bin_name, "peres") == bin_name) exit(peres(argc, argv)); - if (strstr(bin_name, "pesec") == bin_name) exit(pesec(argc, argv)); - if (strstr(bin_name, "pestr") == bin_name) exit(pestr(argc, argv)); - if (strstr(bin_name, "pehash") == bin_name) exit(pehash(argc, argv)); - if (strstr(bin_name, "pepack") == bin_name) exit(pepack(argc, argv)); - if (strstr(bin_name, "pescan") == bin_name) exit(pescan(argc, argv)); - if (strstr(bin_name, "ofs2rva") == bin_name) exit(ofs2rva(argc, argv)); - if (strstr(bin_name, "rva2ofs") == bin_name) exit(rva2ofs(argc, argv)); + if (strstr(bin_name, "peldd") == bin_name) + exit(peldd(argc, argv)); + if (strstr(bin_name, "pestr") == bin_name) + exit(pestr(argc, argv)); + if (strstr(bin_name, "pescan") == bin_name) + exit(pescan(argc, argv)); + if (strstr(bin_name, "pehash") == bin_name) + exit(pehash(argc, argv)); + if (strstr(bin_name, "pesec") == bin_name) + exit(pesec(argc, argv)); + if (strstr(bin_name, "peres") == bin_name) + exit(peres(argc, argv)); + if (strstr(bin_name, "pedis") == bin_name) + exit(pedis(argc, argv)); + if (strstr(bin_name, "pepack") == bin_name) + exit(pepack(argc, argv)); + if (strstr(bin_name, "ofs2rva") == bin_name) + exit(ofs2rva(argc, argv)); + if (strstr(bin_name, "rva2ofs") == bin_name) + exit(rva2ofs(argc, argv)); //------------------------------------------------------------------------// @@ -286,36 +370,64 @@ int main(int argc, char *argv[]) { //------------------------------------------------------------------------// + static const mode_option default_mode[] = { + {"--list", NULL, 'l', 0, NULL, 0}, + {"--help", NULL, 1, 0, NULL, 0}, + {NULL, NULL, 0, 0, NULL, 0} + }; + static const mode_option header_mode[] = { - {"dos", NULL, 0, NULL, MODE_HEADER_DOS }, - {"coff", NULL, 0, NULL, MODE_HEADER_COFF }, - {"optional", NULL, 0, NULL, MODE_HEADER_OPTIONAL}, - {"--list", NULL, 0, NULL, 'l' }, - {"--help", NULL, 0, NULL, 1 }, - {NULL, NULL, 0, NULL, 0 } + {"dos", NULL, MODE_HEADER_DOS, 0, NULL, 0}, + {"coff", NULL, MODE_HEADER_COFF, 0, NULL, 0}, + {"optional", NULL, MODE_HEADER_OPTIONAL, 0, NULL, 0}, + {"--list", NULL, 'l', 0, NULL, 0}, + {"--help", NULL, 1, 0, NULL, 0}, + {NULL, NULL, 0, 0, NULL, 0} + }; + + static const mode_option directory_mode[] = { + {"--list", NULL, 'l', 0, NULL, 0}, + {"--help", NULL, 1, 0, NULL, 0}, + {NULL, NULL, 0, 0, NULL, 0} }; static const mode_option resource_mode[] = { - {"extract", NULL, 0, NULL, COMMAND_EXTRACT}, - {"--info", NULL, 0, NULL, 'i' }, - {"--statistics", NULL, 0, NULL, 's' }, - {"--name-extract", NULL, 0, NULL, 'n' }, - {"--file-version", NULL, 0, NULL, 'v' }, - {"--list", NULL, 0, NULL, 'l' }, - {"--help", NULL, 0, NULL, 1 }, - {NULL, NULL, 0, NULL, 0 } + {"extract", NULL, COMMAND_EXTRACT, 0, NULL, 0}, + {"--info", NULL, 'i', 0, NULL, 0}, + {"--statistics", NULL, 's', 0, NULL, 0}, + {"--name-extract", NULL, 'n', 0, NULL, 0}, + {"--file-version", NULL, 'v', 0, NULL, 0}, + {"--list", NULL, 'l', 0, NULL, 0}, + {"--help", NULL, 1, 0, NULL, 0}, + {NULL, NULL, 0, 0, NULL, 0} }; - static const mode_option directory_mode[] = { - {"--list", NULL, 0, NULL, 'l'}, - {"--help", NULL, 0, NULL, 1 }, - {NULL, NULL, 0, NULL, 0 } + static const mode_option certificate_mode[] = { + {"--list", NULL, 'l', 0, NULL, 0}, + {"--certoutform", NULL, 'c', required_argument, NULL, 0}, + {"--certout", NULL, 'o', required_argument, NULL, 0}, + {"--help", NULL, 1, 0, NULL, 0}, + {NULL, NULL, 0, 0, NULL, 0} + }; + + static const mode_option security_mode[] = { + {"--list", NULL, 'l', 0, NULL, 0}, + {"--help", NULL, 1, 0, NULL, 0}, + {NULL, NULL, 0, 0, NULL, 0} }; static const mode_option section_mode[] = { - {"--list", NULL, 0, NULL, 'l'}, - {"--help", NULL, 0, NULL, 1 }, - {NULL, NULL, 0, NULL, 0 } + {"--list", NULL, 'l', 0, NULL, 0}, + {"--help", NULL, 1, 0, NULL, 0}, + {NULL, NULL, 0, 0, NULL, 0} + }; + + static const mode_option strings_mode[] = { + {"--offset", NULL, 'o', true, &conf.string.offset, VAR_BOOL }, + {"--section", NULL, 's', false, &conf.string.section, VAR_BOOL }, + {"--min-length", NULL, 'n', true, &conf.string.strsize, VAR_UINT16}, + {"--help", NULL, 1, false, NULL, 0 }, + {NULL, NULL, 0, 0, NULL, 0 } }; // static const mode_option hash_mode[] = { @@ -330,23 +442,22 @@ int main(int argc, char *argv[]) { // }; static const mode_option base_mode[] = { - {"headers", header_mode, 0, NULL, MODE_HEADER }, - {"directories", directory_mode, 0, NULL, MODE_DIRECTORY }, - {"exports", NULL, 0, NULL, MODE_EXPORTS }, - {"imports", NULL, 0, NULL, MODE_IMPORTS }, - {"resources", NULL, 0, NULL, MODE_RESOURCES }, - {"certificates", NULL, 0, NULL, MODE_CERTIFICATES}, - {"sections", section_mode, 0, NULL, MODE_SECTION }, - {"libraries", NULL, 0, NULL, MODE_LIBRARY }, - {"strings", NULL, 0, NULL, MODE_STRINGS }, - {"security", NULL, 0, NULL, MODE_SECURITY }, - {"hash", NULL, 0, NULL, COMMAND_HASH }, - {"scan", NULL, 0, NULL, COMMAND_SCAN }, - {"--format", NULL, 0, NULL, 'f' }, - {"--version", NULL, 0, NULL, 'V' }, - {"--complete", NULL, 0, NULL, 2 }, - {"--help", NULL, 0, NULL, 1 }, - {NULL, NULL, 0, NULL, 0 } + {"headers", header_mode, MODE_HEADER, 0, NULL, 0}, + {"directories", directory_mode, MODE_DIRECTORY, 0, NULL, 0}, + {"exports", default_mode, MODE_EXPORTS, 0, NULL, 0}, + {"imports", default_mode, MODE_IMPORTS, 0, NULL, 0}, + {"resources", resource_mode, MODE_RESOURCES, 0, NULL, 0}, + {"certificates", certificate_mode, MODE_CERTIFICATES, 0, NULL, 0}, + {"security", security_mode, MODE_SECURITY, 0, NULL, 0}, + {"sections", section_mode, MODE_SECTION, 0, NULL, 0}, + {"libraries", default_mode, MODE_LIBRARY, 0, NULL, 0}, + {"strings", strings_mode, MODE_STRINGS, 0, NULL, 0}, + {"scan", NULL, COMMAND_SCAN, 0, NULL, 0}, + {"hash", NULL, COMMAND_HASH, 0, NULL, 0}, + {"--format", NULL, 'f', 0, NULL, 0}, + {"--version", NULL, 'V', 0, NULL, 0}, + {"--help", NULL, 1, 0, NULL, 0}, + {NULL, NULL, 0, 0, NULL, 0} }; //------------------------------------------------------------------------// @@ -370,7 +481,8 @@ int main(int argc, char *argv[]) { }; */ - if (argc < 2) help(); + if (argc < 2) + help(); const mode_option *mode = base_mode; int c, f_arg = 0, select = 0, index = 1; @@ -381,7 +493,8 @@ int main(int argc, char *argv[]) { } while ((c = getopt_mode(argc, argv, &mode, &select, &index))) { - if (c < 0) break; + if (c < 0) + break; // printf("%i\n", c); switch (c) { @@ -395,15 +508,14 @@ int main(int argc, char *argv[]) { version(); break; case 'a': - argument_options.all = true; + conf.all = true; + break; + case 'f': + conf.format = "text"; break; - case 'f': - argument_options.format = "text"; - break; case 'l': - argument_options.list = true; + conf.list = true; break; - default: break; // exit(EXIT_FAILURE); @@ -413,7 +525,8 @@ int main(int argc, char *argv[]) { if ((f_arg == 0) && (access(argv[argc - 1], F_OK) == 0)) { f_arg = argc - 1; } - if (f_arg == 0) help(); + if (f_arg == 0) + help(); // printf("%i\n", file); @@ -430,7 +543,8 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - if (!pe_is_pe(&ctx)) EXIT_ERROR("not a valid PE file"); + if (!pe_is_pe(&ctx)) + EXIT_ERROR("not a valid PE file"); pev_config_t config; PEV_INITIALIZE(&config); @@ -438,7 +552,15 @@ int main(int argc, char *argv[]) { output_open_document(); switch (select) { - case 0: + case ARG_NONE: + print_dos_header(&ctx); + print_coff_header(&ctx); + print_optional_header(&ctx); + print_directories(&ctx); + print_imports(&ctx); + print_exports(&ctx); + print_sections(&ctx); + break; case MODE_HEADER: // TODO: --list, --all print_dos_header(&ctx); @@ -454,26 +576,18 @@ int main(int argc, char *argv[]) { case MODE_HEADER_OPTIONAL: print_optional_header(&ctx); break; - - case MODE_DIRECTORY: { - IMAGE_DATA_DIRECTORY **directories = get_pe_directories(&ctx); - if (directories != NULL) print_directories(&ctx); + case MODE_DIRECTORY: + print_directories(&ctx); break; - } - - case MODE_EXPORTS: { - IMAGE_DATA_DIRECTORY **directories = get_pe_directories(&ctx); - if (directories != NULL) print_exports(&ctx); + case MODE_EXPORTS: + print_exports(&ctx); break; - } - - case MODE_IMPORTS: { - IMAGE_DATA_DIRECTORY **directories = get_pe_directories(&ctx); - if (directories != NULL) print_imports(&ctx); + case MODE_IMPORTS: + print_imports(&ctx); break; - } case MODE_RESOURCES: { + // TODO pe_resources_t *resources = pe_resources(&ctx); if (resources == NULL || resources->err != LIBPE_E_OK) { LIBPE_WARNING("This file has no resources"); @@ -485,7 +599,8 @@ int main(int argc, char *argv[]) { peres_show_nodes(&ctx, root_node); peres_show_stats(root_node); peres_show_list(&ctx, root_node); - // peres_save_all_resources(&ctx, root_node, options->namedExtract); + // peres_save_all_resources(&ctx, root_node, + // options->namedExtract); peres_show_version(&ctx, root_node); break; } @@ -495,12 +610,9 @@ int main(int argc, char *argv[]) { case MODE_SECURITY: print_securities(&ctx); // fall through - case MODE_CERTIFICATES: { - // TODO: settings - certificate_settings set = {1, NULL}; - parse_certificates(&set, &ctx); + case MODE_CERTIFICATES: + parse_certificates(&conf.certificate, &ctx); break; - } // case MODE_BASE_RELOCATIONS: // case MODE_DEBUG: @@ -513,14 +625,9 @@ int main(int argc, char *argv[]) { // case MODE_DELAY_IMPORT_DESCRIPTOR: // case MODE_CLR_RUNTIME_HEADER: - case MODE_SECTION: { - if (pe_sections(&ctx) != NULL) - print_sections(&ctx); - else { - LIBPE_WARNING("unable to read sections"); - } + case MODE_SECTION: + print_sections(&ctx); break; - } case MODE_LIBRARY: { IMAGE_DATA_DIRECTORY **directories = pe_directories(&ctx); @@ -533,11 +640,9 @@ int main(int argc, char *argv[]) { break; } - case MODE_STRINGS: { - string_settings set; - print_strings(&ctx, &set); + case MODE_STRINGS: + print_strings(&ctx, &conf.string); break; - } case COMMAND_HASH: // case COMMAND_HASH_MD5: @@ -557,14 +662,13 @@ int main(int argc, char *argv[]) { case COMMAND_DISASSAMBLE: case COMMAND_PACK: - case COMMAND_TRANSFORM: // case COMMAND_ADDRESSING_RELATIVE: // case COMMAND_ADDRESSING_OFFSET: printf("Not Implemented\n"); break; - default: - // printf("Unknown Argument: %d\n", select); - break; + // default: + // // printf("Unknown Argument: %d\n", select); + // break; } output_close_document(); diff --git a/src/output.c b/src/output.c index 860d0baf..88176b52 100644 --- a/src/output.c +++ b/src/output.c @@ -361,8 +361,10 @@ void output_open_scope(const char *scope_name, output_scope_type_e scope_type) { const uint16_t scope_depth = STACK_COUNT(g_scope_stack); output_scope_t * const scope = malloc(sizeof *scope); - if (scope == NULL) + if (scope == NULL) { + fprintf(stderr, "DEBUG: output_open_scope: vscope_depth=%d ABORT\n", STACK_COUNT(g_scope_stack)); abort(); // Abort because it failed miserably! + } scope->name = scope_name == NULL ? NULL : strdup(scope_name); scope->type = scope_type; @@ -374,7 +376,7 @@ void output_open_scope(const char *scope_name, output_scope_type_e scope_type) { scope->parent_type = parent_scope->type; } - //fprintf(stderr, "DEBUG: output_open_scope: scope_depth=%d\n", STACK_COUNT(g_scope_stack)); + // fprintf(stderr, "DEBUG: output_open_scope: scope_depth=%d\n", STACK_COUNT(g_scope_stack)); if (g_format != NULL) g_format->output_fn(g_format, type, scope, key, value); @@ -397,7 +399,7 @@ void output_close_scope(void) { const char *value = NULL; const output_type_e type = OUTPUT_TYPE_SCOPE_CLOSE; - //fprintf(stderr, "DEBUG: output_close_scope: scope_depth=%d\n", STACK_COUNT(g_scope_stack)); + // fprintf(stderr, "DEBUG: output_close_scope: scope_depth=%d\n", STACK_COUNT(g_scope_stack)); if (g_format != NULL) g_format->output_fn(g_format, type, scope, key, value); diff --git a/src/peres.c b/src/peres.c index 2f8c375b..8fd32b3e 100644 --- a/src/peres.c +++ b/src/peres.c @@ -436,7 +436,7 @@ static void peres_restore_resource(peres_resource_restore_t *restore, const pe_r static void peres_save_resource(pe_ctx_t *ctx, const pe_resource_node_t *node, bool namedExtract) { - UNUSED(ctx); + // UNUSED(ctx); assert(node != NULL); assert(node->type == LIBPE_RDT_DATA_ENTRY); assert(node->dirLevel == LIBPE_RDT_LEVEL3); diff --git a/src/readpe.c b/src/readpe.c index 6d8cbf7e..b2611a4e 100644 --- a/src/readpe.c +++ b/src/readpe.c @@ -225,12 +225,17 @@ void print_sections(pe_ctx_t *ctx) output_open_scope("Sections", OUTPUT_SCOPE_TYPE_ARRAY); const uint32_t num_sections = pe_sections_count(ctx); - if (num_sections == 0 || num_sections > MAX_SECTIONS) + if (num_sections == 0 || num_sections > MAX_SECTIONS) { + output_close_scope(); // Sections return; + } IMAGE_SECTION_HEADER **sections = pe_sections(ctx); - if (sections == NULL) + if (sections == NULL) { + LIBPE_WARNING("unable to read sections"); + output_close_scope(); // Sections return; + } static char s[MAX_MSG]; static char section_name_buffer[SECTION_NAME_SIZE+1];