├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── README.md ├── TODO.md ├── arch.c ├── arch.h ├── arch ├── x86_64.c └── x86_64.h ├── binary.c ├── binary.h ├── binutils.h.in ├── debug.h ├── disassemble.c ├── disassemble.h ├── doc ├── optenum.1 ├── optenum.gif └── optenum.webm ├── optenum.c ├── optenum.h ├── optenum.sh.in ├── optenum_config.h.in ├── parsers ├── getopt.c ├── getopt.h ├── glib.c ├── glib.h ├── popt.c └── popt.h └── tests ├── arch_tests.c ├── check_macros.h └── disassemble_tests.c /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles/ 3 | cmake_install.cmake 4 | install_manifest.txt 5 | 6 | Makefile 7 | optenum 8 | optenum_config.h 9 | binutils.h 10 | optenum.sh 11 | 12 | examples/ 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | compiler: 4 | - gcc 5 | 6 | before_install: 7 | - sudo apt-get install binutils binutils-dev check libpopt0 libpopt-dev 8 | 9 | script: 10 | - cmake --version 11 | - cmake -DCMAKE_BUILD_TYPE:STRING=DEBUG -DCMAKE_VERBOSE_MAKEFILE:BOOL=TRUE . 12 | - make 13 | - ldd optenum 14 | - ls -lh optenum 15 | - ./tests/disassemble_tests 16 | 17 | # vim:set tabstop=2 softtabstop=2 shiftwidth=2 expandtab ft=yaml : 18 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013 Matt Boyer. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 2. Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # 3. Neither the name of the project nor the names of its contributors 13 | # may be used to endorse or promote products derived from this software 14 | # without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 17 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | # ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | # SUCH DAMAGE. 27 | 28 | cmake_minimum_required (VERSION 2.6) 29 | project (OPTENUM) 30 | string(TOLOWER ${PROJECT_NAME} EXEC_NAME) 31 | set (VERSION_MAJOR 0) 32 | set (VERSION_MINOR 10) 33 | 34 | # Make the Git revision part of the the version information baked 35 | # into optenum_config.h 36 | execute_process ( COMMAND git log --format=%h -n1 37 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 38 | OUTPUT_VARIABLE GIT_REVISION 39 | ) 40 | set (VERSION_CHANGESET ${GIT_REVISION}) 41 | 42 | configure_file ("${PROJECT_SOURCE_DIR}/optenum_config.h.in" 43 | "${PROJECT_BINARY_DIR}/optenum_config.h") 44 | 45 | configure_file ("${PROJECT_SOURCE_DIR}/optenum.sh.in" 46 | "${PROJECT_BINARY_DIR}/optenum.sh" @ONLY) 47 | 48 | set (dynamic_includes "optenum_config.h" "binutils.h" "optenum.sh") 49 | set_directory_properties (${PROJECT_BINARY_DIR} ADDITIONAL_MAKE_CLEAN_FILES "${dynamic_includes}") 50 | include_directories (${PROJECT_BINARY_DIR}) 51 | 52 | set_property (DIRECTORY . PROPERTY COMPILE_DEFINITIONS_DEBUG "DEBUG") 53 | #add_subdirectory("parsers") 54 | 55 | if (NOT CMAKE_BUILD_TYPE) 56 | message (STATUS "Build type not specified - defaulting to RELEASE build") 57 | set (CMAKE_BUILD_TYPE "RELEASE") 58 | endif (NOT CMAKE_BUILD_TYPE) 59 | 60 | 61 | file(GLOB ${PROJECT_NAME}_SRCS RELATIVE ${PROJECT_SOURCE_DIR} 62 | *.c 63 | # The following are argument parsing modules 64 | parsers/getopt.c 65 | 66 | arch/*.c 67 | ) 68 | 69 | find_package (PkgConfig) 70 | 71 | # Some distros have separate '*-dev' packages for header files, dev docs and 72 | # the like. Consequently, a pkc-config .pc file may be found on the system for 73 | # eg. popt without the requisite header files actually being present. 74 | pkg_check_modules (GLIB2 glib-2.0) 75 | if (GLIB2_FOUND) 76 | find_file(GLIB2_HEADER "glib.h" ${GLIB2_INCLUDE_DIRS}) 77 | if (GLIB2_HEADER) 78 | message(STATUS "Building parser for GLib's g_option_* - header" 79 | " at ${GLIB2_HEADER}") 80 | include_directories (${GLIB2_INCLUDE_DIRS}) 81 | list (APPEND ${PROJECT_NAME}_SRCS parsers/glib.c) 82 | file (APPEND "${PROJECT_BINARY_DIR}/optenum_config.h" 83 | "#define HAVE_GLIB2\n" 84 | ) 85 | else (GLIB2_HEADER) 86 | message(WARNING "glib2 is installed but its header files" 87 | " couldn't be found") 88 | endif (GLIB2_HEADER) 89 | endif (GLIB2_FOUND) 90 | 91 | find_file(POPT_HEADER "popt.h") 92 | if (POPT_HEADER) 93 | message(STATUS "Building parser for popt's poptGetContext - " 94 | "header at ${POPT_HEADER}") 95 | include_directories (${POPT_INCLUDE_DIRS}) 96 | list (APPEND ${PROJECT_NAME}_SRCS parsers/popt.c) 97 | file (APPEND "${PROJECT_BINARY_DIR}/optenum_config.h" 98 | "#define HAVE_POPT\n" 99 | ) 100 | else (POPT_HEADER) 101 | message(WARNING "Header file for popt couldn't be found") 102 | endif (POPT_HEADER) 103 | 104 | find_library(LIBCHECK "check") 105 | find_file(CHECK_HEADER "check.h") 106 | if (LIBCHECK AND CHECK_HEADER) 107 | 108 | if (${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") 109 | message (STATUS "libcheck found and DEBUG build - enabling unit tests") 110 | 111 | file (MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/tests") 112 | file (GLOB ${PROJECT_NAME}_TEST_SRCS RELATIVE "${PROJECT_SOURCE_DIR}/tests" 113 | tests/*.c 114 | ) 115 | 116 | foreach (test_source ${${PROJECT_NAME}_TEST_SRCS}) 117 | string (REPLACE ".c" "" test_executable ${test_source}) 118 | add_executable ("tests/${test_executable}" "tests/${test_source}") 119 | target_link_libraries ("tests/${test_executable}" bfd iberty opcodes check ${CMAKE_DL_LIBS}) 120 | endforeach (test_source ${${PROJECT_NAME}_TEST_SRCS}) 121 | 122 | endif (${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") 123 | else (LIBCHECK AND CHECK_HEADER) 124 | message (STATUS "libcheck not found - no unit tests") 125 | endif (LIBCHECK AND CHECK_HEADER) 126 | 127 | find_file(LIBBFD_HEADER "bfd.h") 128 | if (NOT LIBBFD_HEADER) 129 | message(FATAL_ERROR "libbfd header not found on this system") 130 | endif (NOT LIBBFD_HEADER) 131 | 132 | # 133 | # If at all possible, we want the executable to link against shared objects for 134 | # libbfd and libopcodes. 135 | # Unfortunately, just passing '-lbfd -lopcodes' to the compiler at linking time 136 | # tends to result in a huge executable statically linked against libbfd.a and 137 | # libopcodes.a 138 | # 139 | # This is further complicated by the fact that some distros ship a 140 | # libbfd.so/libopcodes.so file that is *not* an actual shared object, but 141 | # rather a linker script pointing to their respective .a archive. 142 | # 143 | # This means we cannot just give "libbfd.so" as argument to CMake's 144 | # find_library, we'll have to dynamically discover whether there is an "actual" 145 | # shared object with a versioned filename (eg. "libbfd-2.25.so") and give this 146 | # to find_library() with an option to fall back on the .so file in the hope 147 | # that it's a symlink to a real shared object and failing that, the .a 148 | # archive. 149 | # 150 | # At any rate, the variable set by find_library() is then used to set the 151 | # IMPORTED_LOCATION of a CMake "imported" target for that third-party library. 152 | # This ensures the right path is given to the compiler in the make rule that 153 | # links the executable. 154 | # 155 | function (find_binutils_dep libname) 156 | foreach (library_prefix ${CMAKE_SYSTEM_PREFIX_PATH}) 157 | foreach (library_dir "lib64" "lib") 158 | file (GLOB ${libname}_so_path RELATIVE "${library_prefix}/${library_dir}" "${library_prefix}/${library_dir}/lib${libname}-*.so") 159 | if (${libname}_so_path) 160 | break () 161 | endif (${libname}_so_path) 162 | endforeach (library_dir) 163 | if (${libname}_so_path) 164 | break () 165 | endif (${libname}_so_path) 166 | endforeach (library_prefix) 167 | 168 | find_library (${libname}_path ${${libname}_so_path} NAMES "lib${libname}.so" "lib${libname}.a") 169 | if (NOT ${libname}_path) 170 | message(FATAL_ERROR "lib${libname} library not found on this system") 171 | endif (NOT ${libname}_path) 172 | 173 | add_library ("binutils_dep_${libname}" SHARED IMPORTED) 174 | set_target_properties ("binutils_dep_${libname}" PROPERTIES IMPORTED_LOCATION ${${libname}_path}) 175 | endfunction (find_binutils_dep) 176 | 177 | find_binutils_dep (bfd) 178 | find_binutils_dep (opcodes) 179 | 180 | find_file(LIBIBERTY_HEADER "libiberty.h" PATHS /usr/include /usr/include/libiberty) 181 | if (NOT LIBIBERTY_HEADER) 182 | message(FATAL_ERROR "libiberty header not found on this system") 183 | endif (NOT LIBIBERTY_HEADER) 184 | 185 | # Travis CI has CMake version 2.8.7 so we'll use "PATH" instead of "DIRECTORY" 186 | get_filename_component(LIBIBERTY_INCLUDES ${LIBIBERTY_HEADER} "PATH") 187 | include_directories (${LIBIBERTY_INCLUDES}) 188 | 189 | 190 | message(STATUS "Linking against libbfd:\n\tLib: ${bfd_path}\n\tHeader: ${LIBBFD_HEADER}") 191 | message(STATUS "Linking against libopcodes:\n\tLib: ${opcodes_path}") 192 | message(STATUS "Using libiberty header: ${LIBIBERTY_HEADER}") 193 | 194 | if (EXISTS ${LIBIBERTY_INCLUDES}/ansidecl.h) 195 | set (ANSIDECL_SOURCE ${LIBIBERTY_INCLUDES}/ansidecl.h) 196 | else (EXISTS ${LIBIBERTY_INCLUDES}/ansidecl.h) 197 | set (ANSIDECL_SOURCE ansidecl.h) 198 | endif (EXISTS ${LIBIBERTY_INCLUDES}/ansidecl.h) 199 | 200 | configure_file ("${PROJECT_SOURCE_DIR}/binutils.h.in" 201 | "${PROJECT_BINARY_DIR}/binutils.h") 202 | 203 | 204 | add_executable (${EXEC_NAME} ${${PROJECT_NAME}_SRCS}) 205 | target_link_libraries (${EXEC_NAME} binutils_dep_bfd binutils_dep_opcodes ${CMAKE_DL_LIBS}) 206 | 207 | if (CMAKE_COMPILER_IS_GNUCC) 208 | message (STATUS "GCC detected - making compiler pedantic") 209 | set_target_properties (${EXEC_NAME} PROPERTIES COMPILE_FLAGS 210 | "-std=c99 -Wall -pedantic" 211 | ) 212 | endif (CMAKE_COMPILER_IS_GNUCC) 213 | 214 | 215 | 216 | 217 | if (${CMAKE_BUILD_TYPE} STREQUAL "RELEASE") 218 | message (STATUS "Compiling a RELEASE build - enabling install rules") 219 | install(TARGETS ${EXEC_NAME} RUNTIME DESTINATION "bin") 220 | install(FILES "${EXEC_NAME}.sh" DESTINATION "share/optenum") 221 | install(FILES "doc/${EXEC_NAME}.1" DESTINATION "share/man/man1") 222 | else (${CMAKE_BUILD_TYPE} STREQUAL "RELEASE") 223 | message (STATUS "Install rules are only available for RELEASE builds") 224 | endif (${CMAKE_BUILD_TYPE} STREQUAL "RELEASE") 225 | 226 | #enable_testing() 227 | #add_test(NAME foo COMMAND "/usr/bin/false") 228 | 229 | # vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : 230 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #optenum 2 | 3 | optenum(1) is a command-line option enumerator for ELF executables. 4 | 5 | Build status: [![Build Status](https://travis-ci.org/mattboyer/optenum.png)](https://travis-ci.org/mattboyer/optenum) 6 | ![Illustrative video](/doc/optenum.gif?raw=true) 7 | 8 | optenum(1) uses static analysis to extract the options accepted by a binary and lets you use [bash](http://www.gnu.org/software/bash/)'s autocompletion with options. 9 | 10 | ` - ` - it's *magic*! 11 | 12 | ##Installing optenum 13 | 14 | Building optenum is quick and easy. Download the source into a staging directory, run `cmake` then `make install` 15 | 16 | ```shell 17 | $ git clone git://github.com/mattboyer/optenum.git 18 | $ cd optenum/ 19 | $ cmake -DCMAKE_INSTALL_PREFIX:PATH=~/.local . 20 | $ make install 21 | ``` 22 | 23 | **Note** The above will install optenum under the current user's home directory. For a system-wide install, replace the cmake invocation above with `cmake .`. This will cause optenum to be installed under `/usr/local/`, which will require elevated privileges. 24 | 25 | All that's left to do is make sure the completion function is sourced and registered by your shell. You may want to add the following to your `~/.bashrc`: 26 | 27 | ```shell 28 | . ~/.local/share/optenum/optenum.sh 29 | ``` 30 | 31 | ##About 32 | 33 | optenum(1) uses `libbfd` and `libopcodes` from the [GNU binutils](http://www.gnu.org/software/binutils/) package to parse the dynamic symbols used by a binary executable and disassemble its code. 34 | 35 | When it finds a call to one of the supported option-parsing functions below, optenum will attempt to reconstitute the arguments passed as part of the call and, in the event the argument(s) that describes valid options has successfully been retrieved and points to a chunk of data hardcoded in the binary, finally parses it and exposes options to the user. 36 | 37 | optenum(1) **never** executes foreign code and doesn't rely on any particular behaviour in the target binary. No usage message? No problem! 38 | 39 | There are several moving parts and optenum operates on a best-effort basis. When optenum can't retrieve options, it will try to fail gracefully and fail fast. 40 | 41 | ##Support 42 | 43 | ###Option parsing functions 44 | 45 | optenum(1) extracts options from binary executables by relying on the assumption that the task of defining valid command line options is done through a call to one of the following supported functions: 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
FunctionLibraryUsed by
getoptlibcGNU Coreutils, systemd utils, GNU NetCat and many more POSIX-compliant tools
getopt_long
getopt_long_only
g_option_context_add_main_entriesGlibGimp, Xchat,...
poptGetContextpoptSamba utils, logrotate, cryptsetup...
57 | 58 | ###Architectures 59 | 60 | optenum(1) only supports `x86_64` argument passing conventions at this time. 61 | 62 | ##Contact 63 | 64 | Please log any bugs you may encounter using the GitHub [issue tracker](https://github.com/mattboyer/optenum/issues) or alternatively send a mail to `mboyer sdf org`. 65 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - Need to rename some functions and types to avoid semantic confusion. There's a lot of parsing going on but that doesn't mean everything should be called a parser. 2 | 3 | - ~~Make sure the code compiles with all non-standard extensions disabled~~ 4 | -------------------------------------------------------------------------------- /arch.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #include "arch.h" 31 | 32 | /* 33 | We need to implement a dispatcher for every function defined in arch.h. The 34 | dispatcher will look up the correct architecture-specific function in the 35 | optenum executable and call it, passing the arguments it was given. 36 | 37 | We'll use macro expansion in order not to replicate that code for every 38 | function. This means we need a way to make the prototype of the function to 39 | dispatch a parameter to the macro. This is done by passing the following 40 | arguments to the dispatcher macro: 41 | 42 | ARCH_DISPATCH(func_name, return_type, arg1_type, arg2_type, ..., argN_type) 43 | 44 | Since no name is given to the macro, we'll need arbitrary names for the 45 | arguments taken by the expanded dispatcher. We'll use TYPES_NAMES for (arg_type, 46 | arg_name) pairs and NAMES for the names themselves. These macros are taken from 47 | http://stackoverflow.com/a/8814003 48 | 49 | */ 50 | 51 | #define TYPES_NAMES_0() 52 | #define TYPES_NAMES_1(A) A alpha 53 | #define TYPES_NAMES_2(A,B) A alpha, B beta 54 | #define TYPES_NAMES_3(A,B,C) A alpha, B beta, C charlie 55 | #define TYPES_NAMES_4(A,B,C,D) A alpha, B beta, C charlie, D delta 56 | 57 | #define TYPES_NAMES_X(x,A,B,C,D,FUNC, ...) FUNC 58 | 59 | #define TYPES_NAMES(...) TYPES_NAMES_X(,##__VA_ARGS__,\ 60 | TYPES_NAMES_4(__VA_ARGS__),\ 61 | TYPES_NAMES_3(__VA_ARGS__),\ 62 | TYPES_NAMES_2(__VA_ARGS__),\ 63 | TYPES_NAMES_1(__VA_ARGS__),\ 64 | TYPES_NAMES_0(__VA_ARGS__)\ 65 | ) 66 | 67 | 68 | #define NAMES_0() 69 | #define NAMES_1(A) alpha 70 | #define NAMES_2(A,B) alpha, beta 71 | #define NAMES_3(A,B,C) alpha, beta, charlie 72 | #define NAMES_4(A,B,C,D) alpha, beta, charlie, delta 73 | 74 | 75 | 76 | 77 | #define NAMES_X(x,A,B,C,D,FUNC, ...) FUNC 78 | 79 | #define NAMES(...) NAMES_X(,##__VA_ARGS__,\ 80 | NAMES_4(__VA_ARGS__),\ 81 | NAMES_3(__VA_ARGS__),\ 82 | NAMES_2(__VA_ARGS__),\ 83 | NAMES_1(__VA_ARGS__),\ 84 | NAMES_0(__VA_ARGS__)\ 85 | ) 86 | 87 | 88 | #define ARCH_DISPATCH(func_name, return_type, ...) \ 89 | return_type func_name(bfd *target_binary, TYPES_NAMES(__VA_ARGS__)) { \ 90 | \ 91 | if (func_name##_ptr) { \ 92 | debug("Using cached pointer for %s: %016"PRIXPTR"\n", #func_name, \ 93 | (uintptr_t) func_name##_ptr);\ 94 | return func_name##_ptr(NAMES(__VA_ARGS__)); \ 95 | } \ 96 | \ 97 | void *self_dl_handle = dlopen(NULL, RTLD_LAZY); \ 98 | debug("Target compiled for %s\n", \ 99 | target_binary->arch_info->printable_name); \ 100 | return_type (*arch_func_ptr)(__VA_ARGS__); \ 101 | const char *prefix = NULL; \ 102 | \ 103 | if (0==strcmp("i386:x86-64", target_binary->arch_info->printable_name)) \ 104 | prefix = X86_64_PREFIX; \ 105 | if (0==strcmp("i386", target_binary->arch_info->printable_name)) \ 106 | prefix = X86_PREFIX; \ 107 | \ 108 | if (!prefix) { \ 109 | error("Can't find function %s for architecture %s", #func_name, \ 110 | target_binary->arch_info->printable_name);\ 111 | } \ 112 | char *arch_func_name = malloc(200*sizeof(char));\ 113 | strcpy(arch_func_name, prefix);\ 114 | strcat(arch_func_name, #func_name);\ 115 | info("Attempting to resolve %s\n", arch_func_name);\ 116 | *(void**) (&arch_func_ptr) = dlsym(self_dl_handle, arch_func_name); \ 117 | if (!arch_func_ptr) { \ 118 | error("dlsym() failed: %s", dlerror()); \ 119 | } \ 120 | info("Using %s at %016"PRIXPTR"\n", arch_func_name, \ 121 | (uintptr_t) arch_func_ptr);\ 122 | func_name##_ptr = arch_func_ptr;\ 123 | return_type return_value = arch_func_ptr(NAMES(__VA_ARGS__)); \ 124 | dlclose(self_dl_handle); \ 125 | free(arch_func_name);\ 126 | return return_value; \ 127 | } 128 | 129 | 130 | static const char* X86_64_PREFIX = "x86_64__"; 131 | static const char* X86_PREFIX = "i386__"; 132 | 133 | // We'll use these to cache pointers to the arch-specific functions after the 134 | // first successfull dlsym(3) lookup 135 | static bfd_vma (*parse_call_address_ptr)(char*) = NULL; 136 | static bool (*is_call_ptr)(struct disassembly_ring*) = NULL; 137 | static bfd_vma (*parse_ring_for_call_arg_ptr)(const struct disassembly_ring*, 138 | const unsigned int) = NULL; 139 | 140 | // Expand dipatchers for each function declared in arch.h 141 | ARCH_DISPATCH(parse_call_address, bfd_vma, char *) 142 | ARCH_DISPATCH(is_call, bool, struct disassembly_ring *) 143 | ARCH_DISPATCH(parse_ring_for_call_arg, bfd_vma, const struct disassembly_ring *, 144 | const unsigned int) 145 | 146 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 147 | -------------------------------------------------------------------------------- /arch.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __OPTENUM_ARCH_H 31 | #define __OPTENUM_ARCH_H 32 | 33 | #include "binutils.h" 34 | 35 | #include 36 | #include 37 | 38 | #include "disassemble.h" 39 | 40 | 41 | bfd_vma parse_call_address(bfd*, char*); 42 | bool is_call(bfd*, struct disassembly_ring*); 43 | bfd_vma parse_ring_for_call_arg(bfd*, const struct disassembly_ring *, 44 | const unsigned int); 45 | 46 | #endif 47 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 48 | -------------------------------------------------------------------------------- /arch/x86_64.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #include "x86_64.h" 31 | 32 | int x86_64__parse_lea(char* inst_line, char **src, char**dst) { 33 | // This should be changed to LEA 34 | assert(0==strncmp(inst_line, (const char*) "lea", 3)); 35 | inst_line = &inst_line[3]; 36 | unsigned int arg_idx = 0; 37 | 38 | while( (inst_line[arg_idx] != 0x0) && (inst_line[arg_idx] == ' ')) 39 | arg_idx++; 40 | // We've only just skipped past the whitespace to the non-whitespace 41 | // comma-separated operand list 42 | char *inst_args = &inst_line[arg_idx]; 43 | unsigned int arg_separator_idx = 0; 44 | unsigned int comment_char_idx = 0; 45 | 46 | char *lea_src_buffer = xmalloc(DISASSEMBLY_BUFFER_LENGTH); 47 | char *lea_dst_buffer = xmalloc(DISASSEMBLY_BUFFER_LENGTH); 48 | char *lea_comment_buffer = xmalloc(DISASSEMBLY_BUFFER_LENGTH); 49 | 50 | // TODO couldn't we use something like calloc() instead? 51 | memset(lea_src_buffer, 0x0, DISASSEMBLY_BUFFER_LENGTH); 52 | memset(lea_dst_buffer, 0x0, DISASSEMBLY_BUFFER_LENGTH); 53 | memset(lea_comment_buffer, 0x0, DISASSEMBLY_BUFFER_LENGTH); 54 | 55 | while( (inst_args[arg_separator_idx] != 0x0) && (inst_args[arg_separator_idx] != ',')) 56 | arg_separator_idx++; 57 | strncpy(lea_src_buffer, inst_args, arg_separator_idx); 58 | 59 | comment_char_idx = arg_separator_idx; 60 | while( (inst_args[comment_char_idx] != 0x0) && (inst_args[comment_char_idx] != '#')) 61 | comment_char_idx++; 62 | 63 | // This is wrong *and* ugly 64 | unsigned int second_arg_end_idx = arg_separator_idx; 65 | while( (inst_args[second_arg_end_idx] != 0x0) && (inst_args[second_arg_end_idx] != ' ')) 66 | second_arg_end_idx++; 67 | 68 | // This is equally wrong 69 | unsigned int comment_start_idx = comment_char_idx; 70 | while( (inst_args[comment_start_idx] != 0x0) && (inst_args[comment_start_idx] != ' ')) 71 | comment_start_idx++; 72 | 73 | strncpy(lea_dst_buffer, &inst_args[arg_separator_idx+1], (second_arg_end_idx - arg_separator_idx - 1)); 74 | strncpy(lea_comment_buffer, &inst_args[comment_start_idx+1], (DISASSEMBLY_BUFFER_LENGTH - (arg_idx + comment_start_idx))); 75 | 76 | 77 | info("LEA parsed as:\n\tSRC: %s\n\tDST:-%s-\n\tComment: -%s-\n", lea_src_buffer, lea_dst_buffer, lea_comment_buffer); 78 | *src = lea_comment_buffer; 79 | *dst = lea_dst_buffer; 80 | 81 | return 0; 82 | } 83 | 84 | int x86_64__parse_mov(char* inst_line, char **src, char**dst) { 85 | //Do we want the whole line? Yes, I guess we do 86 | assert(0==strncmp(inst_line, (const char*) "mov", 3)); 87 | inst_line = &inst_line[3]; 88 | unsigned int arg_idx = 0; 89 | 90 | while( (inst_line[arg_idx] != 0x0) && (inst_line[arg_idx] == ' ')) 91 | arg_idx++; 92 | 93 | char *inst_args = &inst_line[arg_idx]; 94 | unsigned int arg_separator_idx = 0; 95 | 96 | char *mov_src_buffer = xmalloc(DISASSEMBLY_BUFFER_LENGTH); 97 | char *mov_dst_buffer = xmalloc(DISASSEMBLY_BUFFER_LENGTH); 98 | 99 | memset(mov_src_buffer, 0x0, DISASSEMBLY_BUFFER_LENGTH); 100 | memset(mov_dst_buffer, 0x0, DISASSEMBLY_BUFFER_LENGTH); 101 | 102 | while( (inst_args[arg_separator_idx] != 0x0) && (inst_args[arg_separator_idx] != ',')) 103 | arg_separator_idx++; 104 | 105 | strncpy(mov_src_buffer, inst_args+1, arg_separator_idx); 106 | strncpy(mov_dst_buffer, &inst_args[arg_separator_idx+1], (DISASSEMBLY_BUFFER_LENGTH - (arg_idx + arg_separator_idx))); 107 | 108 | *src = mov_src_buffer; 109 | *dst = mov_dst_buffer; 110 | return 0; 111 | } 112 | 113 | bool x86_64__is_call(struct disassembly_ring *instruction) { 114 | return (0==strncmp(instruction->stream->buffer, (const char*) "call", 4)); 115 | } 116 | 117 | bfd_vma x86_64__parse_call_address(char* call_line) { 118 | assert('c'==call_line[0]); 119 | // As per x86 semantics, there should be one argument right of the call 120 | uint8_t line_idx = 0; 121 | uint8_t token_idx = 1; 122 | 123 | char* address_buf = NULL; 124 | 125 | // TODO Why not use strtok???? 126 | for(line_idx=0; line_idxstream->buffer 172 | ); 173 | 174 | // If we go so far backward that we see another function call, the 175 | // search for argument passing instructions must be called off 176 | if (instruction != first_seen && x86_64__is_call((struct disassembly_ring*) instruction)) 177 | break; 178 | 179 | // This should be the same irrespective of the instruction used to load 180 | // a value into the register 181 | debug("Looking for an instruction loading a value into %s\n", dest_registers[arg_pos]); 182 | 183 | // Argument passing (at least for pointers) is done on x86_64 by 184 | // loading values into registers. 185 | // 186 | // There's a set mapping between the position of the argument in the 187 | // call and which register it will be loaded into. For our purpose, 188 | // we'll consider that the relevant argument is loaded by means of the 189 | // MOV instruction. 190 | // 191 | // It is assumed that the option descriptor passed to the option 192 | // parsing function is a static pointer to a chunk of read-only data in 193 | // the memory space of the binary - ergo we're looking for a hardcoded 194 | // value to be the source operand of the MOV 195 | char *insn_src = NULL; 196 | char *insn_dst = NULL; 197 | // TODO Use case instead!!! 198 | if (0==strncmp(instruction->stream->buffer, (const char*) "mov", 3)) { 199 | 200 | // Read the source and dest of the MOV 201 | x86_64__parse_mov(instruction->stream->buffer, &insn_src, &insn_dst); 202 | debug("MOV src:%s\tdst:%s\n", insn_src, insn_dst); 203 | } 204 | if (0==strncmp(instruction->stream->buffer, (const char*) "lea", 3)) { 205 | // Read the source and dest of the LEA 206 | x86_64__parse_lea(instruction->stream->buffer, &insn_src, &insn_dst); 207 | debug("LEA src:%s\tdst:%s\n", insn_src, insn_dst); 208 | } 209 | if (!(insn_src && insn_dst)) 210 | goto next; 211 | 212 | info("Instruction loaded %s into %s\n", insn_src, insn_dst); 213 | // We'll handle the fact RDX -> EDX -> DX refer to, essentially, the 214 | // same register by matching on the last 2 characters of register 215 | // names. 216 | // TODO What of the upper/lower 8bit registers, eg. DH / DL? 217 | if (0==strncmp(&insn_dst[strlen(insn_dst)-2], dest_registers[arg_pos], strlen(dest_registers[arg_pos]))) { 218 | 219 | // Bail if the argument of index arg_pos isn't a static address 220 | // in the memory space of the program 221 | if (0==strncmp(insn_src, (const char*) "0x", 2)) { 222 | option_descriptor = (bfd_vma) strtoll(&insn_src[2], NULL, 16); 223 | info("Option descriptor at %016"PRIXPTR"\n", (uintptr_t) option_descriptor); 224 | } 225 | } 226 | 227 | free(insn_src); 228 | free(insn_dst); 229 | 230 | 231 | if (option_descriptor) 232 | break; 233 | 234 | next: 235 | instruction = instruction->prev; 236 | if (instruction == first_seen) 237 | break; 238 | instruction_offset--; 239 | } 240 | 241 | info("Arg %d is %016"PRIXPTR"\n", arg_pos, (uintptr_t) option_descriptor); 242 | return option_descriptor; 243 | } 244 | 245 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 246 | -------------------------------------------------------------------------------- /arch/x86_64.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __OPTENUM_ARCH_X86_64_H 31 | #define __OPTENUM_ARCH_X86_64_H 32 | 33 | #include "binutils.h" 34 | #include "../disassemble.h" 35 | 36 | 37 | bfd_vma x86_64__parse_call_address(char*); 38 | 39 | int x86_64__parse_lea(char*, char **, char **); 40 | int x86_64__parse_mov(char*, char **, char **); 41 | bool x86_64__is_call(struct disassembly_ring *); 42 | bfd_vma x86_64__parse_ring_for_call_arg(const struct disassembly_ring *, 43 | const unsigned int); 44 | 45 | static const char *dest_registers[] = {"", "di", "si", "dx", "cx", "r8", "r9"}; 46 | 47 | #endif 48 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 49 | -------------------------------------------------------------------------------- /binary.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | /* Operations performed on the binary go in here */ 31 | #include "binary.h" 32 | 33 | static const char* skip_sections[] = {".init", ".fini", ".plt", NULL}; 34 | 35 | bool is_valid_file(const char* file_name) { 36 | assert(file_name); 37 | struct stat file_props; 38 | 39 | 40 | if (0!=access(file_name, R_OK)) { 41 | error("Couldn't access(2) %s", file_name); 42 | return false; 43 | } 44 | 45 | // Is it a regular file? 46 | if (0!=stat(file_name, &file_props)) { 47 | error("Couldn't stat(2) %s", file_name); 48 | return false; 49 | } 50 | 51 | if (!S_ISREG(file_props.st_mode)) { 52 | error("%s is not a regular file", file_name); 53 | return false; 54 | } 55 | 56 | return true; 57 | } 58 | 59 | size_t parse_binary_dynamic_symbols(bfd *binary_bfd, asymbol ***storage_address) { 60 | long symbol_storage_length = 0; 61 | size_t num_symbols = 0; 62 | asymbol** symbol_storage = NULL; 63 | 64 | assert(binary_bfd); 65 | 66 | /* This will process dynamic symbols only. That's all we want, since 67 | * option parsing will be done by libc/glib/some other external library */ 68 | symbol_storage_length = bfd_get_dynamic_symtab_upper_bound(binary_bfd); 69 | assert(symbol_storage_length > 0); 70 | debug("Symbol storage reqd %ld\n", symbol_storage_length); 71 | 72 | symbol_storage = (asymbol**) xmalloc(symbol_storage_length); 73 | 74 | num_symbols = bfd_canonicalize_dynamic_symtab(binary_bfd, symbol_storage); 75 | assert(num_symbols > 0); 76 | info("Number of symbols copied %ld\n", num_symbols); 77 | 78 | #ifdef DEBUG 79 | unsigned int symbol_idx = 0; 80 | for(symbol_idx = 0; symbol_idx < num_symbols; ++symbol_idx) { 81 | asymbol* sym = symbol_storage[symbol_idx]; 82 | debug("Symbol stored locally at %016" PRIXPTR ":\n\tname %s\n\tflags %04X\n\tvalue %016" PRIXPTR "\n\tunion: %016" PRIXPTR "\n\tin section %016" PRIXPTR " (%s)\n", (uintptr_t) sym, sym->name, sym->flags, (uintptr_t) sym->value, (uintptr_t) &sym->udata, (uintptr_t) sym->section, sym->section->name); 83 | 84 | } 85 | #endif 86 | 87 | // Freeing symbol_storage will have to be done by the caller!!! 88 | *storage_address = symbol_storage; 89 | return num_symbols; 90 | } 91 | 92 | unsigned int parse_dynamic_relocs(bfd *binary_bfd, asymbol **symbols, struct option_parsing_function_list *parsers) { 93 | 94 | unsigned int relocs_found = 0; 95 | 96 | unsigned int relocation_storage_length = 0; 97 | unsigned int num_relocs = 0; 98 | arelent** relocation_storage = NULL; 99 | 100 | unsigned int rel_idx = 0; 101 | 102 | relocation_storage_length = bfd_get_dynamic_reloc_upper_bound(binary_bfd); 103 | info("%d bytes needed to store relocation information for this binary\n", relocation_storage_length); 104 | 105 | // So it's looking like the array of arelent is allocated during the 106 | // call whereas the symbols have to be allocated beforehand?! Would be 107 | // nice to have some documentation, eh? 108 | // WRROOONNNG!!!! It needs to be allocated here!!!! 109 | relocation_storage = (arelent**) xmalloc(relocation_storage_length); // not section_specific 110 | 111 | // The 3rd arg HAS to be the dynamic symtab we've already populated 112 | num_relocs = bfd_canonicalize_dynamic_reloc(binary_bfd, relocation_storage, symbols); 113 | info("Copied %d relocs\n", num_relocs); 114 | 115 | // Parse the relocation entries 116 | for(rel_idx = 0; rel_idx < num_relocs; ++rel_idx) { 117 | asymbol* relocated_symbol = *relocation_storage[rel_idx]->sym_ptr_ptr; 118 | 119 | debug("rel at address %016" PRIXPTR "\n", (uintptr_t) relocation_storage[rel_idx]); 120 | debug("\tfor symbol %s at %016" PRIXPTR "\n", relocated_symbol->name, (uintptr_t) relocated_symbol); 121 | debug("\taddress: %016" PRIXPTR "\n", (uintptr_t) relocation_storage[rel_idx]->address); 122 | debug("\taddend: %016" PRIXPTR "\n", (uintptr_t) relocation_storage[rel_idx]->addend); 123 | 124 | // Compare this reloc against all supported functions 125 | struct option_parsing_function_list *parser_iter = parsers; 126 | while (parser_iter) { 127 | debug("Comparing %s to supported function %s\n", relocated_symbol->name, parser_iter->function->name); 128 | if (0==strcmp(parser_iter->function->name, relocated_symbol->name)) { 129 | info("Found reloc for %s in binary\n", parser_iter->function->name); 130 | parser_iter->reloc = relocation_storage[rel_idx]->address; 131 | relocs_found++; 132 | break; 133 | } 134 | parser_iter = parser_iter->next; 135 | } 136 | } 137 | free(relocation_storage); 138 | 139 | return relocs_found; 140 | } 141 | 142 | bfd_vma get_reloc_call_address(bfd *binary_bfd, bfd_vma reloc) { 143 | 144 | asection *reloc_section = find_vma_section(binary_bfd, reloc); 145 | if (!reloc_section) { 146 | error("Couldn't find section for %016"PRIXPTR, reloc); 147 | return (bfd_vma) NULL; 148 | } 149 | 150 | assert(reloc_section); 151 | 152 | unsigned long section_offset = reloc - reloc_section->vma; 153 | 154 | // Read the whole section, even though we're only going to copy one 155 | // teeny-tiny bfd_vma from it 156 | // TODO This could be made more efficient 157 | bfd_byte *section_buffer = NULL; 158 | bfd_malloc_and_get_section(binary_bfd, reloc_section, §ion_buffer); 159 | 160 | // Copy the reloc's jump address 161 | bfd_vma call_address = (bfd_vma) NULL; 162 | 163 | memcpy(&call_address, section_buffer+section_offset, sizeof(bfd_vma)); 164 | info("Call address: %016" PRIXPTR " (what will be called!)\n", (uintptr_t) call_address); 165 | // TODO Alignment is a problem - the actual argument given to call (on 166 | // x86) may be a few bytes before the jump address 167 | free(section_buffer); 168 | return call_address; 169 | } 170 | 171 | void filter_code_sections(bfd *binary_bfd, struct option_parsing_function_list *parser, struct parsed_option_list **options) { 172 | asection *section = NULL; 173 | section = binary_bfd->sections; 174 | assert(section); 175 | while (section) { 176 | if (!(section->flags & SEC_CODE)) 177 | goto next; 178 | 179 | unsigned int skip_section_idx = 0; 180 | while (skip_sections[skip_section_idx]) { 181 | if (0==strcmp(section->name, skip_sections[skip_section_idx])) { 182 | info("Skipping section %s\n", skip_sections[skip_section_idx]); 183 | goto next; 184 | } 185 | ++skip_section_idx; 186 | } 187 | 188 | debug("Section name %s flags %08X has code\n", section->name, section->flags); 189 | filter_section_for_call(binary_bfd, section, parser, options); 190 | next: 191 | section = section->next; 192 | } 193 | } 194 | 195 | void filter_section_for_call(bfd *binary_bfd, asection *code_section, struct option_parsing_function_list *parser, struct parsed_option_list **options) { 196 | 197 | bool call_found = false; 198 | 199 | // Read the contents of code_section into a buffer 200 | size_t sec_size = bfd_get_section_size(code_section); 201 | bfd_byte *section_data = (bfd_byte *) xmalloc (sec_size); 202 | bfd_get_section_contents(binary_bfd, code_section, section_data, 0, sec_size); 203 | info("Disassembling the contents of section %s (%ld bytes)\n", code_section->name, sec_size); 204 | 205 | // Prepare a ring of file-like buffers into which we'll write the last 206 | // N instructions disassembled from the section 207 | struct disassembly_ring *instruction_ring = prepare_ring(DISASSEMBLY_RING_LENGTH); 208 | 209 | // We let libopcodes' disassembler() decide what disassembly function 210 | // to use based on the binary 211 | disassembler_ftype disasser = disassembler(binary_bfd); 212 | 213 | // We need to prepare a struct disassemble_info - this will control the 214 | // content of the disassembled text printed out by the disassembler 215 | // function pointed to by disasser above 216 | struct disassemble_info *dinfo = (struct disassemble_info *) xmalloc(sizeof(struct disassemble_info)); 217 | 218 | // OK, here's the thing. There is a type named fprintf_ftype for the 219 | // function pointer we're going to use in our struct disassemble_info. 220 | // We'll be passing the struct's 'stream' member as its first argument 221 | // but here's the thing: it doesn't HAVE to work on FILE's. Anything is 222 | // good as long as its consistent! 223 | init_disassemble_info(dinfo, instruction_ring->stream, (fprintf_ftype) buf_fprintf); 224 | 225 | 226 | dinfo->bytes_per_line = 0; 227 | dinfo->bytes_per_chunk = 0; 228 | 229 | // On SL6.6, this somehow ends up being a buffer containing a debug string 230 | dinfo->buffer = section_data; 231 | dinfo->buffer_vma = code_section->vma; 232 | dinfo->buffer_length = sec_size; 233 | dinfo->section = code_section; 234 | 235 | // We need to set the arch and mach members, as we cannot rely on the 236 | // disassembler_ftype function we're going to call to do the right thing 237 | // when these members are 0. Some versions of libopcodes will abort(3) in 238 | // that scenario. 239 | dinfo->arch = bfd_get_arch(binary_bfd); 240 | dinfo->mach = bfd_get_mach(binary_bfd); 241 | 242 | size_t bytes_disassd = 0; 243 | while (bytes_disassdstream->position=0; 247 | dinfo->stream = instruction_ring->stream; 248 | 249 | #ifdef DEBUG 250 | // Save the VMA of the next instruction to decode for debugging 251 | // purposes 252 | bfd_vma code_address = code_section->vma + bytes_disassd; 253 | #endif 254 | 255 | // ...then decode it 256 | bytes_disassd += disasser(code_section->vma + bytes_disassd, dinfo); 257 | 258 | debug("Disassembled %ld/%ld bytes of section %s into %016" 259 | PRIXPTR ":%s\n", bytes_disassd, sec_size, 260 | code_section->name, (uintptr_t) instruction_ring, 261 | instruction_ring->stream->buffer 262 | ); 263 | 264 | if (is_call(binary_bfd, instruction_ring)) { 265 | bfd_vma callee_address = parse_call_address(binary_bfd, instruction_ring->stream->buffer); 266 | 267 | if ((bfd_vma) NULL==callee_address) 268 | continue; 269 | 270 | debug("Call at %016" PRIXPTR " to VMA %016" PRIXPTR "\n", 271 | (uintptr_t) code_address, (uintptr_t) callee_address 272 | ); 273 | 274 | // TODO The way we handle alignment here needs to be looked at 275 | // TODO It should probably be done upstream 276 | if (callee_address == ((parser->call_address >>4)<<4 )) { 277 | info("Candidate function call found in section %s at VMA %016" PRIXPTR "\n", code_section->name, (uintptr_t) code_address); 278 | call_found = true; 279 | 280 | // Extract the value passed to the argument of the option 281 | // parsing function specified in parser->function. It is 282 | // expected that the value we load is a pointer to a static 283 | // chunk of data in the target binary that describes all 284 | // options known to the option parsing function. 285 | if (parser->function->short_option_parser) { 286 | bfd_vma descriptor = parse_ring_for_call_arg(binary_bfd, instruction_ring, parser->function->short_option_descriptor_pos); 287 | 288 | if (descriptor) { 289 | struct parsed_option_list *short_options = parser->function->short_option_parser(binary_bfd, descriptor); 290 | debug("Short option list %016"PRIXPTR"\n", (uintptr_t) short_options); 291 | concatenate_parsed_options(options, short_options); 292 | } 293 | } 294 | 295 | if (parser->function->long_option_parser) { 296 | bfd_vma descriptor = parse_ring_for_call_arg(binary_bfd, instruction_ring, parser->function->long_option_descriptor_pos); 297 | if (descriptor) { 298 | struct parsed_option_list *long_options = parser->function->long_option_parser(binary_bfd, descriptor); 299 | 300 | debug("Long option list %016"PRIXPTR"\n", (uintptr_t) long_options); 301 | concatenate_parsed_options(options, long_options); 302 | } 303 | } 304 | 305 | // Break after first successful match if required 306 | if (break_on_first && NULL != *options) 307 | break; 308 | } 309 | } 310 | 311 | // Rotate the ring 312 | instruction_ring = instruction_ring->next; 313 | } 314 | 315 | if (!call_found) 316 | info("Didn't find any candidates in section %s\n", code_section->name); 317 | 318 | destroy_ring(instruction_ring); 319 | 320 | free(dinfo); 321 | free(section_data); 322 | } 323 | 324 | asection *find_vma_section(bfd *binary_bfd, bfd_vma vma_to_locate) { 325 | assert(binary_bfd); 326 | 327 | asection *section_iterator = binary_bfd->sections; 328 | while (section_iterator) { 329 | if ((vma_to_locate >= section_iterator->vma) && (vma_to_locate <= (section_iterator->vma + section_iterator->size))) 330 | break; 331 | 332 | section_iterator = section_iterator->next; 333 | } 334 | 335 | if (section_iterator) 336 | debug("VMA %016" PRIXPTR " is in section %s\n", 337 | (uintptr_t) vma_to_locate, section_iterator->name); 338 | return section_iterator; 339 | } 340 | 341 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 342 | -------------------------------------------------------------------------------- /binary.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __OPTENUM_BINARY_H 31 | #define __OPTENUM_BINARY_H 32 | 33 | #include "debug.h" //needed for debug stuff 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include // XXX Needed for strcmp. Shouldn't be here!!!! 41 | 42 | #include "binutils.h" 43 | 44 | #include "disassemble.h" 45 | #include "arch.h" 46 | 47 | bool break_on_first; 48 | 49 | // Prototypes go here 50 | size_t parse_binary_dynamic_symbols(bfd *, asymbol ***); 51 | unsigned int parse_dynamic_relocs(bfd *, asymbol **, struct option_parsing_function_list*); 52 | bfd_vma get_reloc_call_address(bfd *, bfd_vma); 53 | 54 | void filter_code_sections(bfd *, struct option_parsing_function_list*, struct parsed_option_list**); 55 | void filter_section_for_call(bfd *, asection *, struct option_parsing_function_list*, struct parsed_option_list**); 56 | asection *find_vma_section(bfd *, bfd_vma); 57 | bool is_valid_file(const char*); 58 | 59 | #endif 60 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 61 | -------------------------------------------------------------------------------- /binutils.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | /* #include the right version of ansidecl.h */ 31 | #include "@ANSIDECL_SOURCE@" 32 | 33 | /* Kludge alert!! 34 | * bfd.h will cause a preprocessor #error if PACKAGE and PACKAGE_VERSION aren't 35 | * defined. 36 | * We'll work around that by temporarily defining then undefining these 37 | * preprocessor macros 38 | */ 39 | #define PACKAGE 40 | #define PACKAGE_VERSION 41 | #include 42 | #undef PACKAGE 43 | #undef PACKAGE_VERSION 44 | 45 | #include 46 | #include 47 | 48 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 49 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __OPTENUM_DEBUG_H 31 | #define __OPTENUM_DEBUG_H 32 | 33 | #include "optenum_config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #ifdef DEBUG 42 | bool debug; 43 | #else 44 | #endif 45 | 46 | 47 | 48 | 49 | /* TODO Write something about PRIXPTR and why we need it */ 50 | 51 | #define stringify_constant(constant) stringify_arg(constant) 52 | #define stringify_arg(arg) #arg 53 | 54 | #define green(string) "\033[0;32m"string"\033[0;m" 55 | #define red(string) "\033[1;31m"string"\033[0;m" 56 | #define white(string) "\033[1;37m"string"\033[0;m" 57 | #define blue(string) "\033[1;34m"string"\033[0;m" 58 | 59 | #ifdef DEBUG 60 | 61 | #define debug_output(level, format, ...) \ 62 | fprintf(stderr, level white("[%s:" stringify_constant(__LINE__) "] ") format, __func__,##__VA_ARGS__) 63 | 64 | #define debug(...) \ 65 | if (debug) debug_output(green("Debug "), ##__VA_ARGS__) 66 | 67 | #define info(...) \ 68 | debug_output(blue("Info "), ##__VA_ARGS__) 69 | 70 | #define _error_0() 71 | #define _error_1(format) \ 72 | if (0!=errno) {\ 73 | fprintf(stderr, red("Error ") white("[%s:" stringify_constant(__LINE__) "] ") format, __func__); \ 74 | perror(" "); \ 75 | } else { \ 76 | fprintf(stderr, red("Error ") white("[%s:" stringify_constant(__LINE__) "] ") format "\n", __func__); \ 77 | } 78 | 79 | #define _error_2(format, arg1) \ 80 | if (0!=errno) {\ 81 | fprintf(stderr, red("Error ") white("[%s:" stringify_constant(__LINE__) "] ") format, __func__, arg1); \ 82 | perror(" "); \ 83 | } else { \ 84 | fprintf(stderr, red("Error ") white("[%s:" stringify_constant(__LINE__) "] ") format "\n", __func__, arg1); \ 85 | } 86 | 87 | #define _error_3(format, arg1, arg2) \ 88 | if (0!=errno) {\ 89 | fprintf(stderr, red("Error ") white("[%s:" stringify_constant(__LINE__) "] ") format, __func__, arg1, arg2); \ 90 | perror(" "); \ 91 | } else { \ 92 | fprintf(stderr, red("Error ") white("[%s:" stringify_constant(__LINE__) "] ") format "\n", __func__, arg1, arg2); \ 93 | } 94 | 95 | #else 96 | #define debug(...) 97 | #define info(...) 98 | 99 | #define _error_0() 100 | #define _error_1(format) \ 101 | if (0!=errno) {\ 102 | fprintf(stderr, stringify_constant(EXEC_NAME) ": " format); \ 103 | perror(" "); \ 104 | } else { \ 105 | fprintf(stderr, stringify_constant(EXEC_NAME) ": " format "\n"); \ 106 | } 107 | 108 | #define _error_2(format, arg1) \ 109 | if (0!=errno) {\ 110 | fprintf(stderr, stringify_constant(EXEC_NAME) ": " format, arg1); \ 111 | perror(" "); \ 112 | } else { \ 113 | fprintf(stderr, stringify_constant(EXEC_NAME) ": " format "\n", arg1); \ 114 | } 115 | 116 | #define _error_3(format, arg1, arg2) \ 117 | if (0!=errno) {\ 118 | fprintf(stderr, stringify_constant(EXEC_NAME) ": " format, arg1, arg2); \ 119 | perror(" "); \ 120 | } else { \ 121 | fprintf(stderr, stringify_constant(EXEC_NAME) ": " format "\n", arg1, arg2); \ 122 | } 123 | 124 | #endif 125 | 126 | #define _error_x(x,A,B,C,FUNC, ...) FUNC 127 | 128 | #define error(...) _error_x(,##__VA_ARGS__,\ 129 | _error_3(__VA_ARGS__),\ 130 | _error_2(__VA_ARGS__),\ 131 | _error_1(__VA_ARGS__),\ 132 | _error_0(__VA_ARGS__),\ 133 | ) 134 | 135 | #endif 136 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 137 | -------------------------------------------------------------------------------- /disassemble.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | /* Disassembly-related code */ 31 | #include "disassemble.h" 32 | 33 | struct disassembly_ring *prepare_ring(unsigned int instructions) { 34 | unsigned int instruction_index = 0; 35 | 36 | struct disassembly_ring *first = NULL; 37 | struct disassembly_ring *prev = NULL; 38 | 39 | struct disassembly_ring *instruction = NULL; 40 | 41 | for(instruction_index=0; instruction_index < instructions; ++instruction_index) { 42 | instruction = (struct disassembly_ring *) xcalloc(1, sizeof(struct disassembly_ring)); 43 | debug("Creating an instruction ring element at %016"PRIXPTR"\n", (uintptr_t) instruction); 44 | 45 | struct disassembly_pseudo_file *disassembly_stream = (struct disassembly_pseudo_file*) xcalloc(1, sizeof(struct disassembly_pseudo_file)); 46 | disassembly_stream->buffer = (char*) xcalloc(DISASSEMBLY_BUFFER_LENGTH, sizeof(char)); 47 | disassembly_stream->position = 0; 48 | instruction->stream = disassembly_stream; 49 | 50 | 51 | 52 | if (0==instruction_index) { 53 | first = instruction; 54 | } else { 55 | instruction->prev = prev; 56 | instruction->prev->next = instruction; 57 | } 58 | 59 | prev = instruction; 60 | } 61 | instruction->next = first; 62 | first->prev = instruction; 63 | 64 | return first; 65 | } 66 | 67 | void destroy_ring(struct disassembly_ring *head) { 68 | struct disassembly_ring* first_seen = head; 69 | while (head) { 70 | debug("Freeing ring element at %016"PRIXPTR"\n", (uintptr_t) head); 71 | struct disassembly_ring* next = head->next; 72 | 73 | free(head->stream->buffer); 74 | free(head->stream); 75 | free(head); 76 | 77 | if (next == first_seen) 78 | break; 79 | head = next; 80 | } 81 | } 82 | 83 | void print_ring(struct disassembly_ring *head) { 84 | struct disassembly_ring* first_seen = head; 85 | while (head) { 86 | info("Instruction ring element at %016"PRIXPTR"\n\t%s\n", (uintptr_t) head, head->stream->buffer); 87 | head = head->prev; 88 | if (head == first_seen) 89 | break; 90 | } 91 | } 92 | 93 | 94 | struct parsed_option_list *append_option(struct parsed_option_list *head, const char *name, bool has_arg, enum OPTION_TYPE type) { 95 | struct parsed_option *new_option = (struct parsed_option*) xcalloc(1, sizeof(struct parsed_option)); 96 | 97 | // We need to copy the name into a new buffer 98 | char *option_name = (char *) xcalloc(OPTION_NAME_LENGTH, sizeof(char)); 99 | strncpy(option_name, name, (size_t) OPTION_NAME_LENGTH); 100 | 101 | new_option->name = option_name; 102 | 103 | //FIXME 104 | new_option->description = NULL; 105 | new_option->takes_argument = has_arg; 106 | new_option->type = type; 107 | 108 | struct parsed_option_list *new_option_list = (struct parsed_option_list*) xcalloc(1, sizeof(struct parsed_option_list)); 109 | new_option_list->option = new_option; 110 | new_option_list->prev = NULL; 111 | new_option_list->next = NULL; 112 | 113 | // Get to the end of the list 114 | while (head && head->next) 115 | head = head->next; 116 | 117 | if (head) { 118 | head->next = new_option_list; 119 | new_option_list->prev = head; 120 | debug("Appended new parsed option after %016"PRIXPTR"\n", (uintptr_t) head); 121 | } else { 122 | head = new_option_list; 123 | } 124 | 125 | return new_option_list; 126 | } 127 | 128 | void concatenate_parsed_options(struct parsed_option_list **left, struct parsed_option_list *right) { 129 | debug("left %016"PRIXPTR" (pointing to %016"PRIXPTR") right %016"PRIXPTR"\n", (uintptr_t) left, (uintptr_t) *left, (uintptr_t) right); 130 | 131 | if (!right) 132 | return; 133 | 134 | // Jump to the start of the right-hand list 135 | while (right->prev) 136 | right = right->prev; 137 | 138 | if (NULL==*left) { 139 | // If there's nothing on the left, make it the right-hand list 140 | *left=right; 141 | } else { 142 | // ...else jump to the end of the left-hand list 143 | struct parsed_option_list *last_left = *left; 144 | while (last_left->next) { 145 | last_left = last_left->next; 146 | } 147 | // ...and link 148 | last_left->next = right; 149 | last_left->next->prev = last_left; 150 | } 151 | } 152 | 153 | void free_parsed_options(struct parsed_option_list *options) { 154 | 155 | if (!options) 156 | return; 157 | 158 | while (options->prev) 159 | options=options->prev; 160 | 161 | while (options) { 162 | free(options->option->name); 163 | free(options->option); 164 | struct parsed_option_list *next = options->next; 165 | free(options); 166 | options = next; 167 | } 168 | } 169 | 170 | void display_options(const struct parsed_option_list *options, enum OPTION_TYPE type) { 171 | 172 | if (!options) 173 | return; 174 | 175 | while (options->prev) 176 | options=options->prev; 177 | 178 | while (options) { 179 | debug("Options %016"PRIXPTR"\n", (uintptr_t) options); 180 | debug("Option %016"PRIXPTR"\n", (uintptr_t) options->option); 181 | debug("Option name %016"PRIXPTR"\n", (uintptr_t) options->option->name); 182 | 183 | if ((type==NO_TYPE)||(type==options->option->type)) { 184 | switch(options->option->type) { 185 | case ONE_DASH: 186 | fprintf(stdout, "-%s\n", options->option->name); 187 | break; 188 | 189 | case TWO_DASH: 190 | fprintf(stdout, "--%s\n", options->option->name); 191 | break; 192 | default: 193 | break; 194 | } 195 | } 196 | options = options->next; 197 | } 198 | } 199 | 200 | 201 | 202 | int buf_fprintf(struct disassembly_pseudo_file* output_stream, const char *format, ...) { 203 | // This function may be called several times for any given call to the 204 | // disassembly function. We need to replicate the behaviour of fprintf 205 | // while printing to a single buffer. 206 | va_list args; 207 | int num_chars_printed=0; 208 | 209 | //debug("call to buf_fprintf pos=%d buffer=%016" PRIXPTR "\n", output_stream->position, (uintptr_t) output_stream->buffer); 210 | 211 | // Clear the buffer if the position has been reset 212 | if (0==output_stream->position) 213 | memset(output_stream->buffer, 0x0, DISASSEMBLY_BUFFER_LENGTH); 214 | 215 | va_start (args, format); 216 | num_chars_printed = vsnprintf (&output_stream->buffer[output_stream->position], (DISASSEMBLY_BUFFER_LENGTH - output_stream->position), format, args); 217 | va_end (args); 218 | if (0 > num_chars_printed) { 219 | error("Couldn't write disassembled instruction token into %016" PRIXPTR, (uintptr_t) output_stream->position); 220 | return -1; 221 | } 222 | 223 | output_stream->position+=num_chars_printed; 224 | return num_chars_printed; 225 | } 226 | 227 | 228 | 229 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 230 | -------------------------------------------------------------------------------- /disassemble.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __OPTENUM_DISASSEMBLE_H 31 | #define __OPTENUM_DISASSEMBLE_H 32 | 33 | 34 | 35 | #include "debug.h" //needed for debug stuff 36 | //#include "binary.h" 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | #include "binutils.h" 43 | 44 | 45 | #define DISASSEMBLY_BUFFER_LENGTH 200 46 | #define DISASSEMBLY_RING_LENGTH 8 47 | 48 | #define OPTION_NAME_LENGTH 50 49 | 50 | struct disassembly_pseudo_file { 51 | char *buffer; 52 | unsigned int position; 53 | }; 54 | 55 | struct disassembly_ring { 56 | struct disassembly_pseudo_file *stream; 57 | struct disassembly_ring *next; 58 | struct disassembly_ring *prev; 59 | }; 60 | 61 | 62 | // This typedef is for the function that will inspect the disassembly ring and populate output option structures 63 | typedef struct parsed_option_list *(*arg_parser) (bfd *, const bfd_vma); 64 | 65 | 66 | struct option_parsing_function { 67 | const char* name; 68 | arg_parser short_option_parser; 69 | const unsigned int short_option_descriptor_pos; 70 | arg_parser long_option_parser; 71 | const unsigned int long_option_descriptor_pos; 72 | }; 73 | 74 | struct option_parsing_function_list { 75 | struct option_parsing_function *function; 76 | bfd_vma reloc; 77 | bfd_vma call_address; 78 | struct option_parsing_function_list *next; 79 | }; 80 | 81 | enum OPTION_TYPE { 82 | ONE_DASH, 83 | TWO_DASH, 84 | NO_DASH, 85 | NO_TYPE, 86 | }; 87 | 88 | struct parsed_option { 89 | char *name; 90 | char *description; 91 | bool takes_argument; 92 | enum OPTION_TYPE type; 93 | }; 94 | 95 | struct parsed_option_list { 96 | struct parsed_option *option; 97 | struct parsed_option_list *prev; 98 | struct parsed_option_list *next; 99 | }; 100 | 101 | struct parsed_option_list *append_option(struct parsed_option_list *, const char *, bool, enum OPTION_TYPE); 102 | void concatenate_parsed_options(struct parsed_option_list **, struct parsed_option_list *); 103 | void free_parsed_options(struct parsed_option_list *); 104 | void display_options(const struct parsed_option_list *, enum OPTION_TYPE); 105 | 106 | 107 | struct disassembly_ring *prepare_ring(unsigned int); 108 | void destroy_ring(struct disassembly_ring *); 109 | void print_ring(struct disassembly_ring *); 110 | 111 | int buf_fprintf(struct disassembly_pseudo_file *, const char *, ...); 112 | 113 | 114 | #endif 115 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 116 | -------------------------------------------------------------------------------- /doc/optenum.1: -------------------------------------------------------------------------------- 1 | .TH OPTENUM "1" "MAY 2013" Linux "User Manuals" 2 | .SH NAME 3 | optenum \- command line option enumerator for ELF executables 4 | .SH SYNOPSIS 5 | .B optenum 6 | [\fI-12blv\fR] 7 | .SH DESCRIPTION 8 | .B optenum 9 | uses static analysis to extract the options accepted by a binary. 10 | 11 | By default, unless \fB-1\fR or \fB-2\fR is passed, optenum will display the set of all command line options accepted by the target binary file. 12 | 13 | .SS "Exit status:" 14 | .TP 15 | 0 16 | if options have been found, 17 | .TP 18 | 1 19 | if no options have been found. 20 | 21 | .SH OPTIONS 22 | .IP -1 23 | Only show options that are prefixed by a single dash character ('-'). 24 | .IP -2 25 | Only show options that are prefixed by two dash characters ('--'). 26 | .IP -b 27 | This option will interrupt the search for command line options after the first successful match. In many cases, using \fB-b\fR causes optenum to terminate much faster as the first call to an option parsing function is often found early in a binary's code section(s), but the set of options presented may be incomplete as a result. 28 | .IP -l 29 | Display the set of option parsing function supported in the target binary and exit. 30 | .IP -v 31 | Print the version number of optenum and exit. 32 | 33 | .SH LIMITATIONS 34 | .B optenum 35 | only supports x86_64 argument passing conventions at this time. 36 | .SH BUGS 37 | Yes 38 | .SH AUTHOR 39 | Matt Boyer 40 | .SH "SEE ALSO" 41 | .BR objdump (1) 42 | -------------------------------------------------------------------------------- /doc/optenum.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattboyer/optenum/d03fa509b74b67f29553ddea8d15ed7fa95f1ca0/doc/optenum.gif -------------------------------------------------------------------------------- /doc/optenum.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattboyer/optenum/d03fa509b74b67f29553ddea8d15ed7fa95f1ca0/doc/optenum.webm -------------------------------------------------------------------------------- /optenum.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #include "optenum.h" 31 | 32 | 33 | void destroy_parser_list(struct option_parsing_function_list *head) { 34 | while(head) { 35 | struct option_parsing_function_list *next= head->next; 36 | debug("Freeing parser list element at %016"PRIXPTR"\n", (uintptr_t) head); 37 | free(head); 38 | head = next; 39 | } 40 | } 41 | 42 | struct option_parsing_function_list *append_option_parser(struct option_parsing_function_list *head, struct option_parsing_function *meta) { 43 | 44 | struct option_parsing_function_list *last = NULL; 45 | struct option_parsing_function_list *first = head; 46 | while (head) { 47 | last = head; 48 | head = head->next; 49 | } 50 | 51 | struct option_parsing_function_list *new_parser = (struct option_parsing_function_list *) xcalloc(1, sizeof(struct option_parsing_function_list)); 52 | 53 | new_parser->function = meta; 54 | debug("Appending %s to supported parsers in list elt %016"PRIXPTR"\n", meta->name, (uintptr_t) new_parser); 55 | if (last) 56 | last->next = new_parser; 57 | if (!first) 58 | first = new_parser; 59 | return first; 60 | } 61 | 62 | struct option_parsing_function_list *prepare_parser_list() { 63 | unsigned int num_parsers = (int) sizeof(parsers)/sizeof(struct option_parsing_function); 64 | info("array of parsers has %u parsers\n", num_parsers); 65 | unsigned int parser_idx = 0; 66 | 67 | struct option_parsing_function_list *parser_list = NULL; 68 | 69 | while(num_parsers > parser_idx) { 70 | parser_list = append_option_parser(parser_list, &parsers[parser_idx]); 71 | parser_idx++; 72 | } 73 | return parser_list; 74 | } 75 | 76 | void print_parsers(struct option_parsing_function_list *head) { 77 | while (head) { 78 | fprintf(stdout, "%s\n", head->function->name); 79 | head = head->next; 80 | } 81 | } 82 | 83 | void print_version() { 84 | printf(stringify_constant(EXEC_NAME) " v%d.%02d.%s\n", VERSION_MAJOR, 85 | VERSION_MINOR, stringify_constant(VERSION_CHANGESET)); 86 | } 87 | 88 | void usage() { 89 | printf("Usage: "stringify_constant(EXEC_NAME) " [-" SHORTOPTS "] \n"); 90 | } 91 | 92 | int main(int argc, char** argv) { 93 | 94 | bfd* binary_bfd = NULL; 95 | 96 | asymbol **dynamic_symbols = NULL; 97 | size_t num_dynamic_symbols = 0; 98 | 99 | #ifdef DEBUG 100 | debug=false; 101 | #endif 102 | 103 | struct option_parsing_function_list *supported_functions = NULL; 104 | supported_functions = prepare_parser_list(); 105 | assert(supported_functions); 106 | 107 | debug("Supported function list starts at %016"PRIXPTR"\n", (uintptr_t) supported_functions); 108 | 109 | break_on_first = false; 110 | 111 | /* Parse options and argument */ 112 | enum OPTION_TYPE parsed_option_display_filter = (enum OPTION_TYPE) NO_TYPE; 113 | int option=0; 114 | opterr = 0; 115 | while ((option = getopt(argc, argv, SHORTOPTS)) != -1) { 116 | switch(option) { 117 | case 'v': 118 | print_version(); 119 | destroy_parser_list(supported_functions); 120 | return EXIT_SUCCESS; 121 | break; 122 | 123 | case 'l': 124 | print_parsers(supported_functions); 125 | destroy_parser_list(supported_functions); 126 | return EXIT_SUCCESS; 127 | break; 128 | #ifdef DEBUG 129 | case 'd': 130 | debug=true; 131 | break; 132 | #endif 133 | case '1': 134 | if (NO_TYPE!=parsed_option_display_filter) { 135 | destroy_parser_list(supported_functions); 136 | error("-1 and -2 are mutually exclusive. Pick one."); 137 | return EXIT_SUCCESS; 138 | } 139 | parsed_option_display_filter = ONE_DASH; 140 | break; 141 | 142 | case '2': 143 | if (NO_TYPE!=parsed_option_display_filter) { 144 | destroy_parser_list(supported_functions); 145 | error("-1 and -2 are mutually exclusive. Pick one."); 146 | return EXIT_SUCCESS; 147 | } 148 | parsed_option_display_filter = TWO_DASH; 149 | break; 150 | 151 | case 'b': 152 | break_on_first = true; 153 | break; 154 | 155 | case '?': 156 | destroy_parser_list(supported_functions); 157 | usage(); 158 | return EXIT_FAILURE; 159 | break; 160 | }; 161 | }; 162 | 163 | 164 | // There needs to be ONE argument after the options 165 | if ((argc-optind) != 1) { 166 | usage(); 167 | destroy_parser_list(supported_functions); 168 | return EXIT_FAILURE; 169 | } 170 | 171 | bfd_init(); 172 | debug("Attempting to open %s\n", argv[optind]); 173 | if (!is_valid_file(argv[optind])) { 174 | destroy_parser_list(supported_functions); 175 | return EXIT_FAILURE; 176 | } 177 | 178 | binary_bfd = bfd_fopen(argv[optind], "default", "r", -1); 179 | info("Operating on %08" PRIXPTR "\n", (uintptr_t) binary_bfd); 180 | 181 | unsigned int is_correct_format = bfd_check_format(binary_bfd, bfd_object); 182 | info("format %d\n", is_correct_format); 183 | 184 | if (0==is_correct_format) { 185 | error("Could not open %s: %s", argv[optind], bfd_errmsg(bfd_get_error())); 186 | goto FAIL; 187 | } 188 | 189 | // Parse dynamic symbols in the target binary 190 | num_dynamic_symbols = parse_binary_dynamic_symbols(binary_bfd, &dynamic_symbols); 191 | if (0>=num_dynamic_symbols) { 192 | error("No dynamic symbols found in %s", argv[optind]); 193 | goto FAIL; 194 | } 195 | 196 | // Parse the dynamic relocations in the target binary and make a note of 197 | // any relocs to one of the supported argument parsing functions 198 | unsigned int interesting_relocs = parse_dynamic_relocs(binary_bfd, dynamic_symbols, supported_functions); 199 | info("%u relocs found in binary\n", interesting_relocs); 200 | free(dynamic_symbols); 201 | 202 | 203 | struct parsed_option_list *options = NULL; 204 | 205 | if (0reloc) 210 | goto next_match; 211 | 212 | info("Looking for calls to:\t%s:\t%016"PRIXPTR"\n", parser_iter->function->name, parser_iter->reloc); 213 | parser_iter->call_address = get_reloc_call_address(binary_bfd, parser_iter->reloc); 214 | 215 | // This will parse all code sections for calls to this parser 216 | filter_code_sections(binary_bfd, parser_iter, &options); 217 | next_match: 218 | parser_iter = parser_iter->next; 219 | } 220 | } else { 221 | error("Couldn't find any relocs to interesting external function calls in %s", argv[optind]); 222 | goto FAIL; 223 | } 224 | 225 | display_options(options, parsed_option_display_filter); 226 | free_parsed_options(options); 227 | 228 | bfd_close(binary_bfd); 229 | destroy_parser_list(supported_functions); 230 | return EXIT_SUCCESS; 231 | FAIL: 232 | bfd_close(binary_bfd); 233 | destroy_parser_list(supported_functions); 234 | return EXIT_FAILURE; 235 | } 236 | 237 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 238 | -------------------------------------------------------------------------------- /optenum.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __OPTENUM_H 31 | #define __OPTENUM_H 32 | 33 | 34 | #include "binutils.h" 35 | #include "debug.h" 36 | #include "binary.h" 37 | 38 | #include 39 | 40 | 41 | #define _SHORTOPTS "12blv" 42 | 43 | #ifdef DEBUG 44 | #define SHORTOPTS _SHORTOPTS "d" 45 | #else 46 | #define SHORTOPTS _SHORTOPTS 47 | #endif 48 | 49 | #include "disassemble.h" 50 | 51 | // Below this line, we'll be including header files for invidal argument 52 | // parsing modules 53 | #include "parsers/getopt.h" 54 | #ifdef HAVE_GLIB2 55 | #include "parsers/glib.h" 56 | #endif 57 | #ifdef HAVE_POPT 58 | #include "parsers/popt.h" 59 | #endif 60 | 61 | struct option_parsing_function parsers[]={ 62 | getopt_parsers 63 | #ifdef HAVE_GLIB2 64 | glib_parsers 65 | #endif 66 | #ifdef HAVE_POPT 67 | popt_parsers 68 | #endif 69 | }; 70 | 71 | 72 | void print_version(); 73 | void usage(); 74 | 75 | #endif 76 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 77 | -------------------------------------------------------------------------------- /optenum.sh.in: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013 Matt Boyer. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 2. Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # 3. Neither the name of the project nor the names of its contributors 13 | # may be used to endorse or promote products derived from this software 14 | # without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 17 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | # ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | # SUCH DAMAGE. 27 | 28 | function __option_complete { 29 | local command=${1} stem=${2} enum_opt option; 30 | case $(type -t ${command}) in 31 | file) 32 | command=$(type -fp ${command});; 33 | *) 34 | return 1;; 35 | esac 36 | # TODO In future, handle the case where the previous token on the 37 | # command line is an option that expects a mandatory argument 38 | [[ ${stem} = -* ]] || return 124 39 | 40 | [[ ${stem%-*} = - ]] && enum_opt="-2" 41 | [[ ${stem%-*} = "" ]] && enum_opt="-1" 42 | 43 | enum_opt="${enum_opt} -b" 44 | while read option; do 45 | [[ ${option} == ${stem}* ]] || continue 46 | COMPREPLY+=(${option}) 47 | done << _EOMAN 48 | $(@CMAKE_INSTALL_PREFIX@/bin/optenum ${enum_opt} ${command} 2>/dev/null) 49 | _EOMAN 50 | } 51 | 52 | complete -D -F __option_complete -o default 53 | 54 | # vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : 55 | -------------------------------------------------------------------------------- /optenum_config.h.in: -------------------------------------------------------------------------------- 1 | // Turn these CMake variables into #defines 2 | #define EXEC_NAME @EXEC_NAME@ 3 | #define VERSION_MAJOR @VERSION_MAJOR@ 4 | #define VERSION_MINOR @VERSION_MINOR@ 5 | #define VERSION_CHANGESET @VERSION_CHANGESET@ 6 | 7 | // Use these to determine whether a given parser is compiled in 8 | -------------------------------------------------------------------------------- /parsers/getopt.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #include "getopt.h" 31 | 32 | struct parsed_option_list *iterate_short_opts(bfd *binary_bfd, const bfd_vma shortopts) { 33 | asection *shortopt_section = find_vma_section(binary_bfd, shortopts); 34 | 35 | // Prepare a list of options for this call 36 | struct parsed_option_list *options_found = NULL; 37 | 38 | size_t sec_size = bfd_get_section_size(shortopt_section); 39 | bfd_byte *section_data = (bfd_byte *) xmalloc (sec_size); 40 | bfd_get_section_contents(binary_bfd, shortopt_section, section_data, 0, sec_size); 41 | debug("Loading the contents of section %s (%ld bytes)\n", shortopt_section->name, sec_size); 42 | 43 | size_t shortopt_offset = (shortopts - shortopt_section->vma); 44 | info("shortopts live in section %s at offset %ld\n", shortopt_section->name, shortopt_offset); 45 | 46 | 47 | bfd_byte *optstring = §ion_data[shortopt_offset]; 48 | info("Short opts %s\n", optstring); 49 | 50 | unsigned int opt_idx = 0; 51 | while (0x0 != optstring[opt_idx]) { 52 | 53 | char opt_char[2] = {optstring[opt_idx], 0x0}; 54 | 55 | bool has_arg = false; 56 | // Does this option require an argument? 57 | if (':' == optstring[opt_idx+1]) { 58 | has_arg = false; 59 | opt_idx++; 60 | } 61 | options_found = append_option(options_found, opt_char, has_arg, ONE_DASH); 62 | opt_idx++; 63 | } 64 | 65 | free(section_data); 66 | return options_found; 67 | } 68 | 69 | struct parsed_option_list *iterate_long_opts(bfd *binary_bfd, const bfd_vma longopts) { 70 | 71 | asection *longopt_section = find_vma_section(binary_bfd, longopts); 72 | 73 | // Prepare a list of options for this call 74 | struct parsed_option_list *options_found = NULL; 75 | 76 | size_t sec_size = bfd_get_section_size(longopt_section); 77 | bfd_byte *section_data = (bfd_byte *) xmalloc (sec_size); 78 | bfd_get_section_contents(binary_bfd, longopt_section, section_data, 0, sec_size); 79 | debug("Loading the contents of section %s (%ld bytes)\n", longopt_section->name, sec_size); 80 | 81 | size_t longopt_offset = (longopts - longopt_section->vma); 82 | info("longopts live in section %s at offset %ld\n", longopt_section->name, longopt_offset); 83 | 84 | const char *all_zeroes = calloc(1, sizeof(struct option)); 85 | 86 | while (longopt_offset < longopt_section->size) { 87 | 88 | /* As per the man page for getopt_long(3), the last element in 89 | * the array of struct option elements has to be all zeroes 90 | */ 91 | if (0==memcmp(§ion_data[longopt_offset], all_zeroes, sizeof(struct option))) 92 | break; 93 | 94 | struct option *long_option = (struct option*) §ion_data[longopt_offset]; 95 | 96 | if (NULL==long_option->name) 97 | break; 98 | 99 | 100 | debug("struct option @ section offset %ld\t loaded into %016" PRIXPTR "\n", longopt_offset, (uintptr_t) long_option); 101 | 102 | // FIXME We're assuming that pointers hardcoded into the binary can 103 | // safely be cast into bfd_vma, which is unsafe 104 | 105 | // We can't assume member 'name' of the struct option will point to an 106 | // area in the same section of the ELF binary as the struct option 107 | // itself 108 | bfd_byte *option_name_section_data = NULL; 109 | asection *option_name_section = find_vma_section(binary_bfd, (bfd_vma) long_option->name); 110 | if (!option_name_section) 111 | break; 112 | debug("option name lives in section %s\n", option_name_section->name); 113 | if (option_name_section==longopt_section) { 114 | option_name_section_data = section_data; 115 | } else { 116 | size_t option_name_sec_size = bfd_get_section_size(option_name_section); 117 | option_name_section_data = (bfd_byte *) xmalloc (option_name_sec_size); 118 | bfd_get_section_contents(binary_bfd, option_name_section, option_name_section_data, 0, option_name_sec_size); 119 | } 120 | 121 | size_t name_offset = ( (bfd_vma)long_option->name - option_name_section->vma); 122 | 123 | debug("option name @ %016" PRIXPTR ": %s\n", (uintptr_t) long_option->name, &option_name_section_data[name_offset]); 124 | 125 | options_found = append_option(options_found, 126 | (const char*) &option_name_section_data[name_offset], 127 | (bool) (1==long_option->has_arg), TWO_DASH); 128 | 129 | if (option_name_section != longopt_section) 130 | free(option_name_section_data); 131 | 132 | longopt_offset += sizeof(struct option); 133 | } 134 | 135 | debug("Last struct option in array at offset %ld vma %016" PRIXPTR "\n", longopt_offset, longopt_section->vma+longopt_offset); 136 | free(section_data); 137 | 138 | return options_found; 139 | } 140 | 141 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 142 | -------------------------------------------------------------------------------- /parsers/getopt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #include "../debug.h" //needed for debug stuff 31 | 32 | #include 33 | #include 34 | 35 | #include "binutils.h" 36 | 37 | // Provides binary access functions 38 | #include "../binary.h" 39 | #include "../disassemble.h" 40 | 41 | // This header file is specific to the function being parsed 42 | #include 43 | 44 | struct parsed_option_list *iterate_long_opts(bfd *, const bfd_vma); 45 | struct parsed_option_list *iterate_short_opts(bfd *, const bfd_vma); 46 | 47 | 48 | // Register the argument parsing functions handled here 49 | #define getopt_parsers\ 50 | {"getopt_long", iterate_short_opts, 3, iterate_long_opts, 4}, \ 51 | {"getopt", iterate_short_opts, 3, NULL, 0}, \ 52 | {"getopt_long_only", iterate_long_opts, 4, NULL, 0}, 53 | 54 | 55 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 56 | -------------------------------------------------------------------------------- /parsers/glib.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #include "glib.h" 31 | 32 | struct parsed_option_list *parse_goption_main_entries(bfd *binary_bfd, const bfd_vma option_entries) { 33 | 34 | asection *option_entry_section = find_vma_section(binary_bfd, option_entries); 35 | 36 | // Prepare a list of options for this call 37 | struct parsed_option_list *options_found = NULL; 38 | 39 | size_t sec_size = bfd_get_section_size(option_entry_section); 40 | bfd_byte *section_data = (bfd_byte *) xmalloc (sec_size); 41 | bfd_get_section_contents(binary_bfd, option_entry_section, section_data, 0, sec_size); 42 | debug("Loading the contents of section %s (%ld bytes)\n", option_entry_section->name, sec_size); 43 | 44 | size_t option_entry_offset = (option_entries - option_entry_section->vma); 45 | info("option_entries live in section %s at offset %ld\n", option_entry_section->name, option_entry_offset); 46 | 47 | 48 | while ((bfd_vma) 0!= section_data[option_entry_offset]) { 49 | 50 | GOptionEntry *glib_option = (GOptionEntry*) §ion_data[option_entry_offset]; 51 | info("GOptionEntry at %016"PRIXPTR"\n", (uintptr_t) glib_option); 52 | 53 | bfd_byte *long_name_section_data = NULL; 54 | asection *long_name_section = find_vma_section(binary_bfd, (bfd_vma) glib_option->long_name); 55 | if (!long_name_section) 56 | break; 57 | debug("option name lives in section %s\n", long_name_section->name); 58 | if (long_name_section == option_entry_section) { 59 | long_name_section_data = section_data; 60 | } else { 61 | size_t long_name_sec_size = bfd_get_section_size(long_name_section); 62 | long_name_section_data = (bfd_byte *) xmalloc (long_name_sec_size); 63 | bfd_get_section_contents(binary_bfd, long_name_section, long_name_section_data, 0, long_name_sec_size); 64 | } 65 | size_t long_option_offset = ((bfd_vma) glib_option->long_name - long_name_section->vma); 66 | 67 | info("long name %s\n", &long_name_section_data[long_option_offset]); 68 | options_found = append_option(options_found, (const char*) &long_name_section_data[long_option_offset], (bool) false, TWO_DASH); 69 | 70 | if ((char) 0x0 != glib_option->short_name) 71 | options_found = append_option(options_found, (const char*) &glib_option->short_name, (bool) false, ONE_DASH); 72 | 73 | option_entry_offset += sizeof(GOptionEntry); 74 | } 75 | 76 | free(section_data); 77 | 78 | return options_found; 79 | } 80 | 81 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 82 | -------------------------------------------------------------------------------- /parsers/glib.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #include "../debug.h" //needed for debug stuff 31 | 32 | #include "binutils.h" 33 | 34 | // Provides binary access functions 35 | #include "../binary.h" 36 | #include "../disassemble.h" 37 | 38 | #include 39 | 40 | struct parsed_option_list *parse_goption_main_entries(bfd *, const bfd_vma); 41 | 42 | #define glib_parsers\ 43 | {"g_option_context_add_main_entries", NULL, 0, parse_goption_main_entries, 2}, 44 | -------------------------------------------------------------------------------- /parsers/popt.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #include "popt.h" 31 | 32 | struct parsed_option_list *parse_popt(bfd *binary_bfd, const bfd_vma option_entries) { 33 | 34 | info("First option at %016"PRIXPTR"\n", option_entries); 35 | 36 | asection *option_entry_section = find_vma_section(binary_bfd, option_entries); 37 | 38 | // Prepare a list of options for this call 39 | struct parsed_option_list *options_found = NULL; 40 | 41 | size_t sec_size = bfd_get_section_size(option_entry_section); 42 | bfd_byte *section_data = (bfd_byte *) xmalloc (sec_size); 43 | bfd_get_section_contents(binary_bfd, option_entry_section, section_data, 0, sec_size); 44 | debug("Loading the contents of section %s (%ld bytes)\n", option_entry_section->name, sec_size); 45 | 46 | size_t option_entry_offset = (option_entries - option_entry_section->vma); 47 | info("option_entries live in section %s at offset %ld\n", option_entry_section->name, option_entry_offset); 48 | 49 | // We'll use POPT_TABLEEND to detect the end of the array 50 | while (0!=bcmp( &last_option, §ion_data[option_entry_offset], sizeof(struct poptOption)) ) { 51 | debug("Inspecting struct poptOption in section %s at offset %ld\n", option_entry_section->name, option_entry_offset); 52 | 53 | struct poptOption *popt_option = (struct poptOption*) §ion_data[option_entry_offset]; 54 | 55 | // Neither the short nor the long name is mandatory in popt options 56 | if (NULL != popt_option->longName) { 57 | info("poptOption at %016"PRIXPTR" long name @ %016"PRIXPTR"\n", (uintptr_t) popt_option, (uintptr_t) popt_option->longName); 58 | 59 | bfd_byte *long_name_section_data = NULL; 60 | asection *long_name_section = find_vma_section(binary_bfd, (bfd_vma) popt_option->longName); 61 | if (!long_name_section) 62 | break; 63 | debug("option name lives in section %s\n", long_name_section->name); 64 | if (long_name_section == option_entry_section) { 65 | long_name_section_data = section_data; 66 | } else { 67 | size_t long_name_sec_size = bfd_get_section_size(long_name_section); 68 | long_name_section_data = (bfd_byte *) xmalloc (long_name_sec_size); 69 | bfd_get_section_contents(binary_bfd, long_name_section, long_name_section_data, 0, long_name_sec_size); 70 | } 71 | size_t long_option_offset = ((bfd_vma) popt_option->longName - long_name_section->vma); 72 | 73 | info("long name %s\n", &long_name_section_data[long_option_offset]); 74 | options_found = append_option(options_found, (const char*) &long_name_section_data[long_option_offset], (bool) false, TWO_DASH); 75 | 76 | } 77 | 78 | if ((char) 0x0 != popt_option->shortName) 79 | options_found = append_option(options_found, (const char*) &popt_option->shortName, (bool) false, ONE_DASH); 80 | 81 | option_entry_offset += sizeof(struct poptOption); 82 | } 83 | 84 | free(section_data); 85 | 86 | return options_found; 87 | 88 | 89 | } 90 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 91 | -------------------------------------------------------------------------------- /parsers/popt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2013 Matt Boyer. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the project nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | */ 29 | 30 | #include "../debug.h" //needed for debug stuff 31 | 32 | #include "binutils.h" 33 | 34 | // Provides binary access functions 35 | #include "../binary.h" 36 | #include "../disassemble.h" 37 | 38 | #include 39 | 40 | #include 41 | 42 | static const struct poptOption last_option = POPT_TABLEEND; 43 | 44 | struct parsed_option_list *parse_popt(bfd *, const bfd_vma); 45 | 46 | #define popt_parsers\ 47 | {"poptGetContext", NULL, 0, parse_popt, 4}, 48 | 49 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 50 | -------------------------------------------------------------------------------- /tests/arch_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../arch.c" 4 | #include "check_macros.h" 5 | 6 | bfd *test_target = NULL; 7 | 8 | void setup (void) { 9 | bfd_init(); 10 | test_target = bfd_fopen((const char*) "/usr/bin/uname", "default", "r", -1); 11 | ck_assert_ptr_ne(test_target, NULL); 12 | ck_assert_str_eq(test_target->filename, "/usr/bin/uname"); 13 | ck_assert_int_ne(test_target->section_count, 0); 14 | 15 | ck_assert_int_ne(test_target->arch_info, 0); 16 | ck_assert_str_eq(test_target->arch_info->printable_name, "foo"); 17 | } 18 | 19 | void teardown (void) { 20 | bfd_close(test_target); 21 | } 22 | 23 | START_TEST (test_is_call) 24 | { 25 | ck_assert_int_eq(parse_call_address(test_target, "callq 0x402460"), 0x402460); 26 | } 27 | END_TEST 28 | 29 | 30 | 31 | 32 | 33 | Suite *arch_suite (void) { 34 | Suite *s = suite_create ("Arch"); 35 | 36 | /* Core test case */ 37 | TCase *tc_core = tcase_create ("Core"); 38 | tcase_add_checked_fixture (tc_core, setup, teardown); 39 | tcase_add_test (tc_core, test_is_call); 40 | suite_add_tcase (s, tc_core); 41 | 42 | return s; 43 | } 44 | 45 | int main (void) { 46 | int number_failed; 47 | Suite *s = arch_suite(); 48 | SRunner *sr = srunner_create (s); 49 | srunner_run_all (sr, CK_NORMAL); 50 | number_failed = srunner_ntests_failed (sr); 51 | srunner_free (sr); 52 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 53 | } 54 | 55 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 56 | -------------------------------------------------------------------------------- /tests/check_macros.h: -------------------------------------------------------------------------------- 1 | #ifndef CHECK_MAJOR_VERSION 2 | #error "check not found" 3 | #endif 4 | 5 | #if CHECK_MICRO_VERSION < 10 6 | 7 | #define ck_assert_int_gt(X, Y) _ck_assert_int(X, >, Y) 8 | /* Pointer comparsion macros with improved output compared to ck_assert(). */ 9 | /* OP may only be == or != */ 10 | #define _ck_assert_ptr(X, OP, Y) do { \ 11 | void* _ck_x = (X); \ 12 | void* _ck_y = (Y); \ 13 | ck_assert_msg(_ck_x OP _ck_y, "Assertion '"#X#OP#Y"' failed: "#X"==%p, "#Y"==%p", _ck_x, _ck_y); \ 14 | } while (0) 15 | #define ck_assert_ptr_eq(X, Y) _ck_assert_ptr(X, ==, Y) 16 | #define ck_assert_ptr_ne(X, Y) _ck_assert_ptr(X, !=, Y) 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /tests/disassemble_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../disassemble.c" 4 | 5 | #include "check_macros.h" 6 | 7 | void setup (void) { 8 | return; 9 | } 10 | 11 | void teardown (void) { 12 | return; 13 | } 14 | 15 | START_TEST (test_append_to_empty) 16 | { 17 | struct parsed_option_list *empty = NULL; 18 | ck_assert_ptr_eq(empty, NULL); 19 | empty = append_option(empty, "foo", true, ONE_DASH); 20 | ck_assert_ptr_ne(empty, NULL); 21 | ck_assert_ptr_eq(empty->prev, NULL); 22 | ck_assert_ptr_eq(empty->next, NULL); 23 | ck_assert_ptr_ne(empty->option, NULL); 24 | 25 | ck_assert_str_eq(empty->option->name, "foo"); 26 | ck_assert_ptr_eq(empty->option->description, NULL); 27 | ck_assert_int_eq(empty->option->takes_argument, true); 28 | ck_assert_int_eq(empty->option->type, ONE_DASH); 29 | } 30 | END_TEST 31 | 32 | START_TEST (test_append_two) 33 | { 34 | struct parsed_option_list *options = NULL; 35 | options = append_option(options, "foo", true, ONE_DASH); 36 | 37 | struct parsed_option_list *option_one= options; 38 | options = append_option(options, "bar", false, TWO_DASH); 39 | 40 | ck_assert_ptr_ne(options, NULL); 41 | ck_assert_ptr_eq(options->prev, option_one); 42 | ck_assert_ptr_eq(options->next, NULL); 43 | ck_assert_ptr_ne(options->option, NULL); 44 | 45 | ck_assert_str_eq(options->option->name, "bar"); 46 | ck_assert_ptr_eq(options->option->description, NULL); 47 | ck_assert_int_eq(options->option->takes_argument, false); 48 | ck_assert_int_eq(options->option->type, TWO_DASH); 49 | 50 | ck_assert_str_eq(options->prev->option->name, "foo"); 51 | ck_assert_ptr_eq(options->prev->option->description, NULL); 52 | ck_assert_int_eq(options->prev->option->takes_argument, true); 53 | ck_assert_int_eq(options->prev->option->type, ONE_DASH); 54 | ck_assert_ptr_eq(options->prev->next, options); 55 | ck_assert_ptr_eq(options->prev->prev, NULL); 56 | } 57 | END_TEST 58 | 59 | START_TEST (test_append_three) 60 | { 61 | struct parsed_option_list *options = NULL; 62 | options = append_option(options, "foo", true, ONE_DASH); 63 | 64 | struct parsed_option_list *option_one= options; 65 | options = append_option(options, "bar", false, TWO_DASH); 66 | struct parsed_option_list *option_two= options; 67 | options = append_option(options, "baz", true, NO_DASH); 68 | 69 | ck_assert_ptr_ne(options, NULL); 70 | 71 | ck_assert_ptr_eq(options->prev, option_two); 72 | ck_assert_ptr_eq(options->next, NULL); 73 | ck_assert_ptr_eq(options->prev->next, options); 74 | ck_assert_ptr_eq(options->prev->prev, option_one); 75 | ck_assert_ptr_eq(options->prev->prev->next, option_two); 76 | ck_assert_ptr_eq(options->prev->prev->next->next, options); 77 | ck_assert_ptr_eq(options->prev->prev->prev, NULL); 78 | 79 | ck_assert_str_eq(options->option->name, "baz"); 80 | ck_assert_ptr_eq(options->option->description, NULL); 81 | ck_assert_int_eq(options->option->takes_argument, true); 82 | ck_assert_int_eq(options->option->type, NO_DASH); 83 | 84 | ck_assert_str_eq(options->prev->option->name, "bar"); 85 | ck_assert_ptr_eq(options->prev->option->description, NULL); 86 | ck_assert_int_eq(options->prev->option->takes_argument, false); 87 | ck_assert_int_eq(options->prev->option->type, TWO_DASH); 88 | 89 | } 90 | END_TEST 91 | 92 | START_TEST (test_concatenate_to_empty) 93 | { 94 | struct parsed_option_list *right = NULL; 95 | right = append_option(right, "foo", true, ONE_DASH); 96 | right = append_option(right, "bar", true, ONE_DASH); 97 | right = append_option(right, "baz", false, TWO_DASH); 98 | 99 | ck_assert_ptr_ne(right, NULL); 100 | 101 | struct parsed_option_list *left = NULL; 102 | concatenate_parsed_options(&left, right); 103 | 104 | ck_assert_ptr_ne(left, NULL); 105 | ck_assert_str_eq(left->option->name, "foo"); 106 | ck_assert_str_eq(left->next->option->name, "bar"); 107 | ck_assert_str_eq(left->next->next->option->name, "baz"); 108 | ck_assert_ptr_eq(left->next->next->next, NULL); 109 | ck_assert_ptr_eq(left->prev, NULL); 110 | } 111 | END_TEST 112 | 113 | START_TEST (test_concatenate_to_nonempty) 114 | { 115 | struct parsed_option_list *right = NULL; 116 | right = append_option(right, "foo", true, ONE_DASH); 117 | right = append_option(right, "bar", true, ONE_DASH); 118 | right = append_option(right, "baz", false, TWO_DASH); 119 | 120 | ck_assert_ptr_ne(right, NULL); 121 | 122 | struct parsed_option_list *left = NULL; 123 | left = append_option(left, "alpha", true, ONE_DASH); 124 | left = append_option(left, "bravo", true, ONE_DASH); 125 | left = append_option(left, "charlie", false, TWO_DASH); 126 | 127 | struct parsed_option_list *old_left = left; 128 | concatenate_parsed_options(&left, right); 129 | // That's really weird. It would be better to change the left list to be 130 | // the head of the concatenated list 131 | ck_assert_ptr_eq(left, old_left); 132 | 133 | ck_assert_ptr_ne(left, NULL); 134 | ck_assert_str_eq(left->option->name, "charlie"); 135 | 136 | ck_assert_str_eq(left->prev->option->name, "bravo"); 137 | ck_assert_str_eq(left->prev->prev->option->name, "alpha"); 138 | 139 | ck_assert_str_eq(left->next->option->name, "foo"); 140 | ck_assert_str_eq(left->next->next->option->name, "bar"); 141 | ck_assert_str_eq(left->next->next->next->option->name, "baz"); 142 | 143 | ck_assert_ptr_eq(left->next->next->next->next, NULL); 144 | ck_assert_ptr_eq(left->prev->prev->prev, NULL); 145 | } 146 | END_TEST 147 | 148 | 149 | START_TEST (test_free_options_appended) 150 | { 151 | struct parsed_option_list *options = NULL; 152 | options = append_option(options, "foo", true, ONE_DASH); 153 | 154 | struct parsed_option_list *option_one= options; 155 | options = append_option(options, "bar", false, TWO_DASH); 156 | struct parsed_option_list *option_two= options; 157 | options = append_option(options, "baz", true, NO_DASH); 158 | 159 | ck_assert_ptr_ne(options, NULL); 160 | ck_assert_ptr_eq(options->prev, option_two); 161 | ck_assert_ptr_eq(options->prev->prev, option_one); 162 | 163 | ck_assert_ptr_ne(options->option->name, NULL); 164 | ck_assert_ptr_ne(options->prev->option->name, NULL); 165 | ck_assert_ptr_ne(options->prev->prev->option->name, NULL); 166 | 167 | free_parsed_options(options); 168 | ck_assert_ptr_ne(options, NULL); 169 | ck_assert_ptr_eq(options->prev, option_two); 170 | ck_assert_ptr_eq(options->prev->prev, option_one); 171 | // How can we assert that the calls to free() have been successfull? 172 | } 173 | END_TEST 174 | 175 | 176 | Suite *arch_suite (void) { 177 | Suite *s = suite_create ("Options"); 178 | 179 | TCase *tc_core = tcase_create ("Appending"); 180 | tcase_add_checked_fixture (tc_core, setup, teardown); 181 | tcase_add_test (tc_core, test_append_to_empty); 182 | tcase_add_test (tc_core, test_append_two); 183 | tcase_add_test (tc_core, test_append_three); 184 | suite_add_tcase (s, tc_core); 185 | 186 | TCase *tc_append = tcase_create ("Concatenating"); 187 | tcase_add_checked_fixture (tc_core, setup, teardown); 188 | tcase_add_test (tc_core, test_concatenate_to_empty); 189 | tcase_add_test (tc_core, test_concatenate_to_nonempty); 190 | suite_add_tcase (s, tc_append); 191 | 192 | TCase *tc_free = tcase_create ("Freeing"); 193 | tcase_add_checked_fixture (tc_core, setup, teardown); 194 | tcase_add_test (tc_core, test_free_options_appended); 195 | suite_add_tcase (s, tc_free); 196 | 197 | return s; 198 | } 199 | 200 | int main (void) { 201 | int number_failed; 202 | Suite *s = arch_suite(); 203 | SRunner *sr = srunner_create (s); 204 | srunner_run_all (sr, CK_NORMAL); 205 | number_failed = srunner_ntests_failed (sr); 206 | srunner_free (sr); 207 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 208 | } 209 | 210 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ 211 | --------------------------------------------------------------------------------