Path: blob/main/contrib/elftoolchain/strings/strings.c
39536 views
/*-1* Copyright (c) 2007 S.Sam Arun Raj2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/types.h>27#include <sys/capsicum.h>28#include <sys/stat.h>2930#include <capsicum_helpers.h>31#include <ctype.h>32#include <err.h>33#include <errno.h>34#include <fcntl.h>35#include <getopt.h>36#include <inttypes.h>37#include <stdint.h>38#include <stdio.h>39#include <stdlib.h>40#include <string.h>41#include <sysexits.h>42#include <unistd.h>4344#include <libelf.h>45#include <libelftc.h>46#include <gelf.h>4748#include <libcasper.h>49#include <casper/cap_fileargs.h>5051#include "_elftc.h"5253ELFTC_VCSID("$Id: strings.c 3648 2018-11-22 23:26:43Z emaste $");5455enum radix_style {56RADIX_DECIMAL,57RADIX_HEX,58RADIX_OCTAL59};6061enum encoding_style {62ENCODING_7BIT,63ENCODING_8BIT,64ENCODING_16BIT_BIG,65ENCODING_16BIT_LITTLE,66ENCODING_32BIT_BIG,67ENCODING_32BIT_LITTLE68};6970#define PRINTABLE(c) \71((c) >= 0 && (c) <= 255 && \72((c) == '\t' || isprint((c)) || \73(encoding == ENCODING_8BIT && (c) > 127)))7475static int encoding_size, entire_file, show_filename, show_loc;76static enum encoding_style encoding;77static enum radix_style radix;78static intmax_t min_len;7980static struct option strings_longopts[] = {81{ "all", no_argument, NULL, 'a'},82{ "bytes", required_argument, NULL, 'n'},83{ "encoding", required_argument, NULL, 'e'},84{ "help", no_argument, NULL, 'h'},85{ "print-file-name", no_argument, NULL, 'f'},86{ "radix", required_argument, NULL, 't'},87{ "version", no_argument, NULL, 'v'},88{ NULL, 0, NULL, 0 }89};9091int getcharacter(FILE *, long *);92int handle_file(fileargs_t *fa, const char *);93int handle_elf(const char *, FILE *);94int handle_binary(const char *, FILE *, size_t);95int find_strings(const char *, FILE *, off_t, off_t);96void show_version(void);97void usage(void);9899/*100* strings(1) extracts text(contiguous printable characters)101* from elf and binary files.102*/103int104main(int argc, char **argv)105{106fileargs_t *fa;107cap_rights_t rights;108int ch, rc;109110rc = 0;111min_len = 0;112encoding_size = 1;113if (elf_version(EV_CURRENT) == EV_NONE)114errx(EXIT_FAILURE, "ELF library initialization failed: %s",115elf_errmsg(-1));116117while ((ch = getopt_long(argc, argv, "1234567890ae:fhn:ot:Vv",118strings_longopts, NULL)) != -1) {119switch ((char)ch) {120case 'a':121entire_file = 1;122break;123case 'e':124if (*optarg == 's') {125encoding = ENCODING_7BIT;126} else if (*optarg == 'S') {127encoding = ENCODING_8BIT;128} else if (*optarg == 'b') {129encoding = ENCODING_16BIT_BIG;130encoding_size = 2;131} else if (*optarg == 'B') {132encoding = ENCODING_32BIT_BIG;133encoding_size = 4;134} else if (*optarg == 'l') {135encoding = ENCODING_16BIT_LITTLE;136encoding_size = 2;137} else if (*optarg == 'L') {138encoding = ENCODING_32BIT_LITTLE;139encoding_size = 4;140} else141usage();142/* NOTREACHED */143break;144case 'f':145show_filename = 1;146break;147case 'n':148min_len = strtoimax(optarg, (char**)NULL, 10);149if (min_len <= 0)150errx(EX_USAGE, "option -n should specify a "151"positive decimal integer.");152break;153case 'o':154show_loc = 1;155radix = RADIX_OCTAL;156break;157case 't':158show_loc = 1;159if (*optarg == 'd')160radix = RADIX_DECIMAL;161else if (*optarg == 'o')162radix = RADIX_OCTAL;163else if (*optarg == 'x')164radix = RADIX_HEX;165else166usage();167/* NOTREACHED */168break;169case 'v':170case 'V':171show_version();172/* NOTREACHED */173case '0':174case '1':175case '2':176case '3':177case '4':178case '5':179case '6':180case '7':181case '8':182case '9':183min_len *= 10;184min_len += ch - '0';185break;186case 'h':187case '?':188default:189usage();190/* NOTREACHED */191}192}193argc -= optind;194argv += optind;195196cap_rights_init(&rights, CAP_READ, CAP_SEEK, CAP_FSTAT, CAP_FCNTL, CAP_MMAP_R);197fa = fileargs_init(argc, argv, O_RDONLY, 0, &rights, FA_OPEN);198if (fa == NULL)199err(1, "Unable to initialize casper fileargs");200201caph_cache_catpages();202if (caph_limit_stdio() < 0 || caph_enter_casper() < 0) {203fileargs_free(fa);204err(1, "Unable to enter capability mode");205}206207if (min_len == 0)208min_len = 4;209if (*argv == NULL)210rc = find_strings("{standard input}", stdin, 0, 0);211else while (*argv != NULL) {212if (handle_file(fa, *argv) != 0)213rc = 1;214argv++;215}216217fileargs_free(fa);218219return (rc);220}221222int223handle_file(fileargs_t *fa, const char *name)224{225FILE *pfile;226int rt;227228if (name == NULL)229return (1);230pfile = fileargs_fopen(fa, name, "rb");231if (pfile == NULL) {232warnx("'%s': %s", name, strerror(errno));233return (1);234}235236rt = handle_elf(name, pfile);237fclose(pfile);238return (rt);239}240241/*242* Files not understood by handle_elf, will be passed off here and will243* treated as a binary file. This would include text file, core dumps ...244*/245int246handle_binary(const char *name, FILE *pfile, size_t size)247{248249(void)fseeko(pfile, 0, SEEK_SET);250return (find_strings(name, pfile, 0, size));251}252253/*254* Will analyse a file to see if it ELF, other files including ar(1),255* core dumps are passed off and treated as flat binary files. Unlike256* GNU size in FreeBSD this routine will not treat ELF object from257* different archs as flat binary files(has to overridden using -a).258*/259int260handle_elf(const char *name, FILE *pfile)261{262struct stat buf;263GElf_Ehdr elfhdr;264GElf_Shdr shdr;265Elf *elf;266Elf_Scn *scn;267int rc, fd;268269rc = 0;270fd = fileno(pfile);271if (fstat(fd, &buf) < 0)272return (1);273274/* If entire file is chosen, treat it as a binary file */275if (entire_file)276return (handle_binary(name, pfile, buf.st_size));277278(void)lseek(fd, 0, SEEK_SET);279elf = elf_begin(fd, ELF_C_READ, NULL);280if (elf_kind(elf) != ELF_K_ELF) {281(void)elf_end(elf);282return (handle_binary(name, pfile, buf.st_size));283}284285if (gelf_getehdr(elf, &elfhdr) == NULL) {286(void)elf_end(elf);287warnx("%s: ELF file could not be processed", name);288return (1);289}290291if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) {292(void)elf_end(elf);293return (handle_binary(name, pfile, buf.st_size));294} else {295scn = NULL;296while ((scn = elf_nextscn(elf, scn)) != NULL) {297if (gelf_getshdr(scn, &shdr) == NULL)298continue;299if (shdr.sh_type != SHT_NOBITS &&300(shdr.sh_flags & SHF_ALLOC) != 0) {301rc = find_strings(name, pfile, shdr.sh_offset,302shdr.sh_size);303}304}305}306(void)elf_end(elf);307return (rc);308}309310/*311* Retrieves a character from input stream based on the encoding312* type requested.313*/314int315getcharacter(FILE *pfile, long *rt)316{317int i, c;318char buf[4];319320for(i = 0; i < encoding_size; i++) {321c = getc(pfile);322if (c == EOF)323return (-1);324buf[i] = c;325}326327switch (encoding) {328case ENCODING_7BIT:329case ENCODING_8BIT:330*rt = buf[0];331break;332case ENCODING_16BIT_BIG:333*rt = (buf[0] << 8) | buf[1];334break;335case ENCODING_16BIT_LITTLE:336*rt = buf[0] | (buf[1] << 8);337break;338case ENCODING_32BIT_BIG:339*rt = ((long) buf[0] << 24) | ((long) buf[1] << 16) |340((long) buf[2] << 8) | buf[3];341break;342case ENCODING_32BIT_LITTLE:343*rt = buf[0] | ((long) buf[1] << 8) | ((long) buf[2] << 16) |344((long) buf[3] << 24);345break;346default:347return (-1);348}349350return (0);351}352353/*354* Input stream is read until the end of file is reached or until355* the section size is reached in case of ELF files. Contiguous356* characters of >= min_size(default 4) will be displayed.357*/358int359find_strings(const char *name, FILE *pfile, off_t offset, off_t size)360{361off_t cur_off, start_off;362char *obuf;363long c;364int i;365366if ((obuf = (char*)calloc(1, min_len + 1)) == NULL) {367fprintf(stderr, "Unable to allocate memory: %s\n",368strerror(errno));369return (1);370}371372(void)fseeko(pfile, offset, SEEK_SET);373cur_off = offset;374start_off = 0;375for (;;) {376if ((offset + size) && (cur_off >= offset + size))377break;378start_off = cur_off;379memset(obuf, 0, min_len + 1);380for(i = 0; i < min_len; i++) {381if (getcharacter(pfile, &c) < 0)382goto _exit1;383if (PRINTABLE(c)) {384obuf[i] = c;385obuf[i + 1] = 0;386cur_off += encoding_size;387} else {388if (encoding == ENCODING_8BIT &&389(uint8_t)c > 127) {390obuf[i] = c;391obuf[i + 1] = 0;392cur_off += encoding_size;393continue;394}395cur_off += encoding_size;396break;397}398}399400if (i >= min_len && ((cur_off <= offset + size) ||401!(offset + size))) {402if (show_filename)403printf("%s: ", name);404if (show_loc) {405switch (radix) {406case RADIX_DECIMAL:407printf("%7ju ", (uintmax_t)start_off);408break;409case RADIX_HEX:410printf("%7jx ", (uintmax_t)start_off);411break;412case RADIX_OCTAL:413printf("%7jo ", (uintmax_t)start_off);414break;415}416}417printf("%s", obuf);418419for (;;) {420if ((offset + size) &&421(cur_off >= offset + size))422break;423if (getcharacter(pfile, &c) < 0)424break;425cur_off += encoding_size;426if (encoding == ENCODING_8BIT &&427(uint8_t)c > 127) {428putchar(c);429continue;430}431if (!PRINTABLE(c))432break;433putchar(c);434}435putchar('\n');436}437}438_exit1:439free(obuf);440return (0);441}442443#define USAGE_MESSAGE "\444Usage: %s [options] [file...]\n\445Print contiguous sequences of printable characters.\n\n\446Options:\n\447-a | --all Scan the entire file for strings.\n\448-e ENC | --encoding=ENC Select the character encoding to use.\n\449-f | --print-file-name Print the file name before each string.\n\450-h | --help Print a help message and exit.\n\451-n N | --bytes=N | -N Print sequences with 'N' or more characters.\n\452-o Print offsets in octal.\n\453-t R | --radix=R Print offsets using the radix named by 'R'.\n\454-v | --version Print a version identifier and exit.\n"455456void457usage(void)458{459460fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());461exit(EXIT_FAILURE);462}463464void465show_version(void)466{467468printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());469exit(EXIT_SUCCESS);470}471472473