├── .gitignore ├── Makefile.am ├── README.md ├── configure.ac ├── elfcat.1 └── elfcat.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | elfcat 3 | 4 | # autotools stuff: 5 | Makefile.in 6 | aclocal.m4 7 | autom4te.cache 8 | compile 9 | config.h.in 10 | configure 11 | depcomp 12 | install-sh 13 | missing 14 | .deps 15 | Makefile 16 | config.h 17 | config.log 18 | config.status 19 | stamp-h1 20 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS= elfcat 2 | elfcat_SOURCES= elfcat.c 3 | 4 | elfcat_CFLAGS= $(AM_CFLAGS) 5 | elfcat_LDADD= $(AM_LDADD) 6 | 7 | elfcat_CFLAGS+= $(LIBELF_CFLAGS) 8 | elfcat_LDADD+= $(LIBELF_LIBS) 9 | 10 | man_MANS = elfcat.1 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elfcat 2 | 3 | Dump sections or program entries from a ELF file. 4 | 5 | ~~~sh 6 | elfcat ./foo --section-name .text 7 | elfcat ./foo --section-index 3 8 | elfcat ./foo --program-index 9 | ~~~ 10 | 11 | ## Installation 12 | 13 | ### Manual 14 | 15 | ~~~sh 16 | autoreconf -fiv && ./configure && make 17 | ~~~ 18 | 19 | ### NetBSD 20 | 21 | Installation on NetBSD from prebuilt packages: 22 | 23 | ~~~sh 24 | pkgin install elfcat 25 | ~~~ 26 | 27 | Installation from sources: 28 | 29 | ~~~sh 30 | cd /usr/pkgsrc/devel/elfcat && make install 31 | ~~~ 32 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([elfcat], [1.0.0], []) 2 | AM_INIT_AUTOMAKE([-Wall -Werror foreign]) 3 | 4 | AC_CONFIG_HEADERS([config.h]) 5 | 6 | AC_PROG_CC_C99 7 | AC_GNU_SOURCE 8 | AC_SYS_LARGEFILE 9 | 10 | PKG_CHECK_MODULES([LIBELF], [libelf], [have_libelf=yes], [have_libelf=no]) 11 | 12 | if test "x$have_libelf" = "xno"; then 13 | AC_CHECK_LIB([elf], [elf_begin], [have_libelf=yes], []) 14 | AC_CHECK_HEADER([libelf.h], [], [have_libelf=no]) 15 | 16 | if test "x$have_libelf" = "xno"; then 17 | AC_MSG_ERROR([*** libelf support not found]) 18 | fi 19 | 20 | LIBELF_LIBS=-lelf 21 | LIBELF_CFLAGS= 22 | fi 23 | 24 | AC_SUBST([LIBELF_CFLAGS]) 25 | AC_SUBST([LIBELF_LIBS]) 26 | 27 | AC_CONFIG_FILES([ 28 | Makefile 29 | ]) 30 | AC_OUTPUT 31 | -------------------------------------------------------------------------------- /elfcat.1: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2016 Gabriel Corona. MIT License. 2 | .Dd January 16, 2016 3 | .Dt ELFCAT 1 4 | .Os 5 | .Sh NAME 6 | .Nm elfcat 7 | .Nd dump sections, segments of an ELF file 8 | .Sh SYNOPSIS 9 | .Nm 10 | .Op Fl -help 11 | .Op Fl -version 12 | .Op Fl -section-name Ar name 13 | .Op Fl -section-index Ar index 14 | .Op Fl -program-index Ar 15 | .Op Ar elf-names 16 | .Sh DESCRIPTION 17 | .Nm 18 | dumps requested parts (sections and segments) of an ELF file 19 | (executable, shared object, \.o file) to stdout. 20 | .Pp 21 | Dumping sections can be achieved with 22 | .Qq objcopy --dump-section 23 | but this tools relies on BFD which abstracts 24 | all executable files and is not able to see some ELF sections. 25 | Moreover, it cannot handle program header table entries. 26 | .Pp 27 | Supported options are: 28 | .Bl -tag -width "--section-index index" -compact 29 | .It Fl -help 30 | Help 31 | .It Fl -version 32 | Version information 33 | .It Fl -section-name Ar name 34 | Dump a section with a given 35 | .Ar name . 36 | .It Fl -section-index Ar index 37 | Dump a section found by its 38 | .Ar index 39 | in the section table. 40 | .It Fl -program-index 41 | Dump a part of the ELF file described by a program header found by its 42 | index. 43 | .El 44 | .Sh EXIT STATUS 45 | If everything goes well, 46 | .Nm 47 | exits with 0. 48 | If anything bad happens, it exits with a nonzero value. 49 | .Sh EXAMPLES 50 | .Bd -literal -offset indent 51 | elfcat --section-name .dynstr /bin/sh | tr '\\000' '\\n' 52 | .Ed 53 | .Pp 54 | Print the content of the dynamic string table. Each entry is 55 | terminated by a 56 | .Va NUL 57 | byte in the 58 | .Vt FILE 59 | so we use 60 | .Qq tr 61 | in order to display it in a more human-friendly form. 62 | .Sh SEE ALSO 63 | .Lk https://github.com/randomstuff/elfcat/ "Git repo" 64 | .Sh STANDARDS 65 | .Lk http://www.sco.com/developers/devspecs/gabi41.pdf "System V Application Binary Interface, Edition 4.1" 66 | .Sh AUTHORS 67 | Written by 68 | .An -nosplit 69 | .An Gabriel Corona 70 | and 71 | .An Kamil Rytarowski . 72 | .Pp 73 | Copyright (C) 2016 Gabriel Corona. MIT License. 74 | -------------------------------------------------------------------------------- /elfcat.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Gabriel Corona 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | 43 | // Genetic dump: 44 | 45 | static int dump_zero(off_t size) 46 | { 47 | size_t buffer_size = 64*1024; 48 | void* buffer = calloc(1, buffer_size); 49 | while(size) { 50 | size_t count = buffer_size < size ? buffer_size : size; 51 | size_t wcount = write(STDOUT_FILENO, buffer, count); 52 | if (wcount == -1) { 53 | fprintf(stderr, "Could not write\n"); 54 | free(buffer); 55 | return EXIT_FAILURE; 56 | } 57 | size -= wcount; 58 | } 59 | free(buffer); 60 | return EXIT_SUCCESS; 61 | } 62 | 63 | static int dump_data(int fd, off_t offset, off_t size) 64 | { 65 | size_t buffer_size = 64*1024; 66 | void* buffer = malloc(buffer_size); 67 | off_t start = lseek(fd, 0, SEEK_CUR); 68 | if (start == -1) { 69 | fprintf(stderr, "Could not seek\n"); 70 | goto err_free; 71 | } 72 | if (lseek(fd, offset, SEEK_SET) == -1) { 73 | fprintf(stderr, "Could not seek\n"); 74 | goto err_lseek; 75 | } 76 | 77 | while(size) { 78 | size_t available_size = buffer_size < size ? buffer_size : size; 79 | size_t rcount = read(fd, buffer, available_size); 80 | if (rcount == -1) { 81 | fprintf(stderr, "Could not read data\n"); 82 | goto err_lseek; 83 | } 84 | size -= rcount; 85 | 86 | char* data = buffer; 87 | while(rcount) { 88 | size_t wcount = write(STDOUT_FILENO, data, rcount); 89 | if (wcount == -1) { 90 | fprintf(stderr, "Could not write data\n"); 91 | goto err_lseek; 92 | } 93 | rcount -= wcount; 94 | data += wcount; 95 | } 96 | } 97 | 98 | if (lseek(fd, start, SEEK_SET) == -1) { 99 | fprintf(stderr, "Could not seek\n"); 100 | goto err_free; 101 | } 102 | free(buffer); 103 | return EXIT_SUCCESS; 104 | 105 | err_lseek: 106 | lseek(fd, start, SEEK_SET); 107 | err_free: 108 | free(buffer); 109 | return EXIT_FAILURE; 110 | } 111 | 112 | // Program entries: 113 | 114 | static int dump_program_by_index(Elf* elf, int fd, size_t index) 115 | { 116 | GElf_Phdr phdr; 117 | if (gelf_getphdr(elf, index, &phdr) != &phdr) { 118 | fprintf(stderr, "Program entry not found\n"); 119 | return EXIT_FAILURE; 120 | } 121 | int res = dump_data(fd, phdr.p_offset, phdr.p_filesz); 122 | if (res) { 123 | fprintf(stderr, "Could not dump data from file\n"); 124 | return EXIT_FAILURE; 125 | } 126 | return dump_zero(phdr.p_memsz - phdr.p_filesz); 127 | } 128 | 129 | // Sections: 130 | 131 | static int dump_section(int fd, Elf_Scn* scn) 132 | { 133 | GElf_Shdr shdr; 134 | if (gelf_getshdr(scn , &shdr) != &shdr) { 135 | fprintf(stderr, "Could not get section data\n"); 136 | return EXIT_FAILURE; 137 | } 138 | if (shdr.sh_type == SHT_NOBITS) 139 | return dump_zero(shdr.sh_size); 140 | return dump_data(fd, shdr.sh_offset, shdr.sh_size); 141 | } 142 | 143 | static int dump_section_by_index(Elf* elf, int fd, size_t index) 144 | { 145 | Elf_Scn* scn = elf_getscn(elf, index); 146 | if (scn == NULL) { 147 | fprintf(stderr, "Section not found\n"); 148 | return EXIT_FAILURE; 149 | } 150 | return dump_section(fd, scn); 151 | } 152 | 153 | static int dump_section_by_name(Elf* elf, int fd, const char* section_name) 154 | { 155 | Elf_Scn* scn = NULL; 156 | GElf_Shdr shdr; 157 | 158 | size_t shstrndx; 159 | if (elf_getshdrstrndx(elf, &shstrndx) != 0) { 160 | fprintf(stderr, "Could not find section name section\n"); 161 | return EXIT_FAILURE; 162 | } 163 | 164 | while ((scn = elf_nextscn(elf, scn)) != NULL) { 165 | if (gelf_getshdr(scn , &shdr) != &shdr) { 166 | fprintf(stderr, "Could not get section data\n"); 167 | return EXIT_FAILURE; 168 | } 169 | const char* scn_name = elf_strptr(elf, shstrndx , shdr.sh_name); 170 | if (scn_name == NULL) { 171 | fprintf(stderr, "Could not get section name\n"); 172 | return EXIT_FAILURE; 173 | } 174 | if (strcmp(scn_name, section_name) == 0) 175 | return dump_section(fd, scn); 176 | } 177 | fprintf(stderr, "Section not found\n"); 178 | return EXIT_FAILURE; 179 | } 180 | 181 | // CLI: 182 | 183 | enum cli_option { 184 | CLI_OPTION_HELP = 500, 185 | CLI_OPTION_SECTION_NAME = 501, 186 | CLI_OPTION_SECTION_INDEX = 502, 187 | CLI_OPTION_PROGRAM_INDEX = 503, 188 | CLI_OPTION_VERSION = 504, 189 | }; 190 | 191 | struct cli_action { 192 | enum cli_option option; 193 | const char* argument; 194 | }; 195 | 196 | static int process_file( 197 | char* elf_file_name, struct cli_action* actions, size_t actions_count) 198 | { 199 | int res = EXIT_SUCCESS; 200 | int elf_fd = -1; 201 | Elf* elf = NULL; 202 | 203 | elf_fd = open(elf_file_name, O_RDONLY); 204 | if (elf_fd == -1) { 205 | fprintf(stderr, "Could not open file %s\n", elf_file_name); 206 | res = EXIT_FAILURE; 207 | goto on_failure; 208 | } 209 | 210 | elf = elf_begin(elf_fd, ELF_C_READ, NULL); 211 | if (elf == NULL) { 212 | fprintf(stderr, "Could read file %s as ELF\n (%s)", 213 | elf_file_name, elf_errmsg(elf_errno())); 214 | res = EXIT_FAILURE; 215 | goto on_failure; 216 | } 217 | 218 | // Do each action: 219 | for (size_t i = 0; i < actions_count ; ++i) { 220 | switch (actions[i].option) { 221 | case CLI_OPTION_SECTION_NAME: 222 | if (dump_section_by_name(elf, elf_fd, actions[i].argument)) 223 | res = EXIT_FAILURE; 224 | break; 225 | case CLI_OPTION_SECTION_INDEX: 226 | if (dump_section_by_index(elf, elf_fd, atoll(actions[i].argument))) 227 | res = EXIT_FAILURE; 228 | break; 229 | case CLI_OPTION_PROGRAM_INDEX: 230 | if (dump_program_by_index(elf, elf_fd, atoll(actions[i].argument))) 231 | return EXIT_FAILURE; 232 | break; 233 | default: 234 | fputs("Unexpected action\n", stderr); 235 | abort(); 236 | break; 237 | } 238 | } 239 | 240 | on_failure: 241 | if (elf) 242 | elf_end(elf); 243 | if (elf_fd != -1) 244 | close(elf_fd); 245 | return res; 246 | } 247 | 248 | static int show_help(void) 249 | { 250 | fprintf(stderr, "elfcat [options] [elf-files]\n" 251 | " --help This help\n" 252 | " --version Version\n" 253 | " --section-name .text Dump a section with a given name\n" 254 | " --section-index 3 Dump a section with a given index\n" 255 | " --program-index 3 Dump a program header with a given index\n"); 256 | return EXIT_SUCCESS; 257 | } 258 | 259 | static int show_version(void) 260 | { 261 | fprintf(stderr, PACKAGE_STRING "\n" 262 | "Copyright 2015-2016 Gabriel Corona\n" 263 | "MIT License\n"); 264 | return EXIT_SUCCESS; 265 | } 266 | 267 | int main(int argc, char** argv) 268 | { 269 | size_t actions_reserved = 5; 270 | struct cli_action* actions; 271 | size_t actions_count = 0; 272 | 273 | actions = malloc(sizeof(struct cli_action) * actions_reserved); 274 | if (actions == NULL) { 275 | fputs("Allocation error\n", stderr); 276 | abort(); 277 | } 278 | 279 | // CLI processing: 280 | while (1) { 281 | static struct option long_options[] = { 282 | {"help", no_argument, NULL, CLI_OPTION_HELP}, 283 | {"version", no_argument, NULL, CLI_OPTION_VERSION}, 284 | {"section-name", required_argument, NULL, CLI_OPTION_SECTION_NAME}, 285 | {"section-index", required_argument, NULL, CLI_OPTION_SECTION_INDEX}, 286 | {"program-index", required_argument, NULL, CLI_OPTION_PROGRAM_INDEX}, 287 | {0, 0, 0, 0} 288 | }; 289 | int option = getopt_long(argc, argv, "", long_options, NULL); 290 | if (option == -1) 291 | break; 292 | switch (option) { 293 | case '?': 294 | exit(EXIT_FAILURE); 295 | break; 296 | case CLI_OPTION_HELP: 297 | return show_help(); 298 | case CLI_OPTION_VERSION: 299 | return show_version(); 300 | 301 | case CLI_OPTION_SECTION_NAME: 302 | case CLI_OPTION_SECTION_INDEX: 303 | case CLI_OPTION_PROGRAM_INDEX: 304 | if (actions_reserved == actions_count) { 305 | actions_reserved = actions_reserved + actions_reserved / 2; 306 | actions = realloc(actions, sizeof(struct cli_action) * actions_reserved); 307 | if (actions == NULL) { 308 | fputs("Allocation error\n", stderr); 309 | abort(); 310 | } 311 | } 312 | actions[actions_count].option = option; 313 | actions[actions_count].argument = optarg; 314 | actions_count++; 315 | break; 316 | default: 317 | fputs("?\n", stderr); 318 | abort(); 319 | break; 320 | } 321 | } 322 | 323 | // Initialize libelf: 324 | if (elf_version(EV_CURRENT) == EV_NONE) { 325 | fprintf(stderr, "Could not initialize libelf\n"); 326 | return EXIT_FAILURE; 327 | } 328 | 329 | // Process each file: 330 | int res = EXIT_SUCCESS; 331 | while (optind < argc) 332 | if (process_file(argv[optind++], actions, actions_count)) 333 | res = EXIT_FAILURE; 334 | free(actions); 335 | return res; 336 | } 337 | --------------------------------------------------------------------------------