source: trunk/src/sstrip.c @ 452

Last change on this file since 452 was 286, checked in by katerina, 12 years ago

Fix for ticket #211 (samhain_kmem compile problems) and ticket #210 (line length, filename quoting in config file).

File size: 13.8 KB
Line 
1/* sstrip, version 2.0: Copyright (C) 1999-2001 by Brian Raiter, under the
2 * GNU General Public License. No warranty. See LICENSE for details.
3 */
4
5/* Modified for portability and 64bit/32bit elf executables, Rainer Wichmann */
6 
7#include "config.h"
8
9#include        <stdio.h>
10#include        <stdlib.h>
11#include        <string.h>
12#include        <errno.h>
13#include        <unistd.h>
14#include        <fcntl.h>
15
16#if !defined(__ia64)  && !defined(__ia64__)  && !defined(__itanium__) &&  \
17    !defined(__alpha) && !defined(__alpha__) && \
18    (defined(HAVE_ELF_H) || defined(HAVE_LINUX_ELF_H)) && \
19    (defined(__linux__)  || defined(__FreeBSD__)) && \
20    (defined(__i386__)   || defined(__i386) || defined(i386))
21
22/* || defined(__sun) || defined(__sun__) || defined(sun) */
23
24
25#if defined(HAVE_ELF_H)
26#include        <elf.h>
27#else
28#include        <linux/elf.h>
29#endif
30
31#ifndef TRUE
32#define TRUE            1
33#define FALSE           0
34#endif
35
36#ifndef ELFCLASS32
37#define ELFCLASS32      1               /* 32-bit objects */
38#endif
39#ifndef ELFCLASS64
40#define ELFCLASS64      2               /* 64-bit objects */
41#endif
42
43
44
45/* The name of the program.
46 */
47static char const      *progname;
48
49/* The name of the current file.
50 */
51static char const      *filename;
52
53
54/* A simple error-handling function. FALSE is always returned for the
55 * convenience of the caller.
56 */
57static int err(char const *errmsg)
58{
59    fprintf(stderr, "%s: %s: %s\n", progname, filename, errmsg);
60    return FALSE;
61}
62
63/* A macro for I/O errors: The given error message is used only when
64 * errno is not set.
65 */
66#define ferr(msg)       (err(errno ? strerror(errno) : (msg)))
67
68/* readelfheader() reads the ELF header into our global variable, and
69 * checks to make sure that this is in fact a file that we should be
70 * munging.
71 */
72static int readelfheader_32(int fd, Elf32_Ehdr *ehdr)
73{
74    errno = 0;
75    if (read(fd, ehdr, sizeof *ehdr) != sizeof *ehdr)
76        return ferr("missing or incomplete ELF header.");
77
78    /* Check the ELF signature.
79     */
80    if (!(ehdr->e_ident[EI_MAG0] == ELFMAG0 &&
81          ehdr->e_ident[EI_MAG1] == ELFMAG1 &&
82          ehdr->e_ident[EI_MAG2] == ELFMAG2 &&
83          ehdr->e_ident[EI_MAG3] == ELFMAG3))
84        return err("missing ELF signature.");
85
86    /* Compare the file's class and endianness with the program's.
87     */
88#ifdef ELF_DATA
89    if (ehdr->e_ident[EI_DATA] != ELF_DATA)
90        return err("ELF file has different endianness.");
91#endif
92
93    if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
94        return FALSE;
95
96    /* Check the target architecture.
97     */
98#ifdef ELF_ARCH
99    if (ehdr->e_machine != ELF_ARCH)
100        return err("ELF file created for different architecture.");
101#endif
102
103    /* Verify the sizes of the ELF header and the program segment
104     * header table entries.
105     */
106    if (ehdr->e_ehsize != sizeof(Elf32_Ehdr))
107        return err("unrecognized ELF header size.");
108    if (ehdr->e_phentsize != sizeof(Elf32_Phdr))
109        return err("unrecognized program segment header size.");
110
111    /* Finally, check the file type.
112     */
113    if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
114        return err("not an executable or shared-object library.");
115
116    return TRUE;
117}
118
119static int readelfheader_64(int fd, Elf64_Ehdr *ehdr)
120{
121    errno = 0;
122
123    if (lseek(fd, 0, SEEK_SET))
124        return ferr("could not rewind file");
125
126    if (read(fd, ehdr, sizeof *ehdr) != sizeof *ehdr)
127        return ferr("missing or incomplete ELF header.");
128
129    /* Check the ELF signature.
130     */
131    if (!(ehdr->e_ident[EI_MAG0] == ELFMAG0 &&
132          ehdr->e_ident[EI_MAG1] == ELFMAG1 &&
133          ehdr->e_ident[EI_MAG2] == ELFMAG2 &&
134          ehdr->e_ident[EI_MAG3] == ELFMAG3))
135        return err("missing ELF signature.");
136
137    /* Compare the file's class and endianness with the program's.
138     */
139#ifdef ELF_DATA
140    if (ehdr->e_ident[EI_DATA] != ELF_DATA)
141        return err("ELF file has different endianness.");
142#endif
143
144    if (ehdr->e_ident[EI_CLASS] != ELFCLASS64)
145        return err("ELF file has different word size.");
146
147    /* Check the target architecture.
148     */
149#ifdef ELF_ARCH
150    if (ehdr->e_machine != ELF_ARCH)
151        return err("ELF file created for different architecture.");
152#endif
153
154    /* Verify the sizes of the ELF header and the program segment
155     * header table entries.
156     */
157    if (ehdr->e_ehsize != sizeof(Elf64_Ehdr))
158        return err("unrecognized ELF header size.");
159    if (ehdr->e_phentsize != sizeof(Elf64_Phdr))
160        return err("unrecognized program segment header size.");
161
162    /* Finally, check the file type.
163     */
164    if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
165        return err("not an executable or shared-object library.");
166
167    return TRUE;
168}
169
170/* readphdrtable() loads the program segment header table into memory.
171 */
172static int readphdrtable_32(int fd, Elf32_Ehdr const *ehdr, Elf32_Phdr **phdrs)
173{
174    size_t      size;
175
176    if (!ehdr->e_phoff || !ehdr->e_phnum)
177        return err("ELF file has no program header table.");
178
179    size = ehdr->e_phnum * sizeof **phdrs;
180    if (!(*phdrs = malloc(size)))
181        return err("Out of memory!");
182
183    errno = 0;
184    if (read(fd, *phdrs, size) != (ssize_t)size)
185        return ferr("missing or incomplete program segment header table.");
186
187    return TRUE;
188}
189
190static int readphdrtable_64(int fd, Elf64_Ehdr const *ehdr, Elf64_Phdr **phdrs)
191{
192    size_t      size;
193
194    if (!ehdr->e_phoff || !ehdr->e_phnum)
195        return err("ELF file has no program header table.");
196
197    size = ehdr->e_phnum * sizeof **phdrs;
198    if (!(*phdrs = malloc(size)))
199        return err("Out of memory!");
200
201    errno = 0;
202    if (read(fd, *phdrs, size) != (ssize_t)size)
203        return ferr("missing or incomplete program segment header table.");
204
205    return TRUE;
206}
207
208/* getmemorysize() determines the offset of the last byte of the file
209 * that is referenced by an entry in the program segment header table.
210 * (Anything in the file after that point is not used when the program
211 * is executing, and thus can be safely discarded.)
212 */
213static int getmemorysize_32(Elf32_Ehdr const *ehdr, Elf32_Phdr const *phdrs,
214                            unsigned long *newsize)
215{
216    Elf32_Phdr   const   *phdr;
217    unsigned long       size, n;
218    unsigned int        i;
219
220    /* Start by setting the size to include the ELF header and the
221     * complete program segment header table.
222     */
223    size = ehdr->e_phoff + ehdr->e_phnum * sizeof *phdrs;
224    if (size < sizeof *ehdr)
225        size = sizeof *ehdr;
226
227    /* Then keep extending the size to include whatever data the
228     * program segment header table references.
229     */
230    for (i = 0, phdr = phdrs ; i < ehdr->e_phnum ; ++i, ++phdr) {
231        if (phdr->p_type != PT_NULL) {
232            n = phdr->p_offset + phdr->p_filesz;
233            if (n > size)
234                size = n;
235        }
236    }
237
238    *newsize = size;
239    return TRUE;
240}
241
242static int getmemorysize_64(Elf64_Ehdr const *ehdr, Elf64_Phdr const *phdrs,
243                            unsigned long *newsize)
244{
245    Elf64_Phdr   const   *phdr;
246    unsigned long         size, n;
247    unsigned int          i;
248
249    /* Start by setting the size to include the ELF header and the
250     * complete program segment header table.
251     */
252    size = ehdr->e_phoff + ehdr->e_phnum * sizeof *phdrs;
253    if (size < sizeof *ehdr)
254        size = sizeof *ehdr;
255
256    /* Then keep extending the size to include whatever data the
257     * program segment header table references.
258     */
259    for (i = 0, phdr = phdrs ; i < ehdr->e_phnum ; ++i, ++phdr) {
260        if (phdr->p_type != PT_NULL) {
261            n = phdr->p_offset + phdr->p_filesz;
262            if (n > size)
263                size = n;
264        }
265    }
266
267    *newsize = size;
268    return TRUE;
269}
270
271/* truncatezeros() examines the bytes at the end of the file's
272 * size-to-be, and reduces the size to exclude any trailing zero
273 * bytes.
274 */
275static int truncatezeros(int fd, unsigned long *newsize)
276{
277    unsigned char       contents[1024];
278    unsigned long       size, n;
279
280    size = *newsize;
281    do {
282        n = sizeof contents;
283        if (n > size)
284            n = size;
285        if (lseek(fd, size - n, SEEK_SET) == (off_t)-1)
286            return ferr("cannot seek in file.");
287        if (read(fd, contents, n) != (ssize_t)n)
288            return ferr("cannot read file contents");
289        while (n && !contents[--n])
290            --size;
291    } while (size && !n);
292
293    /* Sanity check.
294     */
295    if (!size)
296        return err("ELF file is completely blank!");
297
298    *newsize = size;
299    return TRUE;
300}
301
302/* modifyheaders() removes references to the section header table if
303 * it was stripped, and reduces program header table entries that
304 * included truncated bytes at the end of the file.
305 */
306static int modifyheaders_32(Elf32_Ehdr *ehdr, Elf32_Phdr *phdrs,
307                            unsigned long newsize)
308{
309    Elf32_Phdr   *phdr;
310    unsigned int  i;
311
312    /* If the section header table is gone, then remove all references
313     * to it in the ELF header.
314     */
315    if (ehdr->e_shoff >= newsize) {
316        ehdr->e_shoff = 0;
317        ehdr->e_shnum = 0;
318        ehdr->e_shentsize = 0;
319        ehdr->e_shstrndx = 0;
320    }
321
322    /* The program adjusts the file size of any segment that was
323     * truncated. The case of a segment being completely stripped out
324     * is handled separately.
325     */
326    for (i = 0, phdr = phdrs ; i < ehdr->e_phnum ; ++i, ++phdr) {
327        if (phdr->p_offset >= newsize) {
328            phdr->p_offset = newsize;
329            phdr->p_filesz = 0;
330        } else if (phdr->p_offset + phdr->p_filesz > newsize) {
331            phdr->p_filesz = newsize - phdr->p_offset;
332        }
333    }
334
335    return TRUE;
336}
337
338static int modifyheaders_64(Elf64_Ehdr *ehdr, Elf64_Phdr *phdrs,
339                            unsigned long newsize)
340{
341    Elf64_Phdr   *phdr;
342    unsigned int  i;
343
344    /* If the section header table is gone, then remove all references
345     * to it in the ELF header.
346     */
347    if (ehdr->e_shoff >= newsize) {
348        ehdr->e_shoff = 0;
349        ehdr->e_shnum = 0;
350        ehdr->e_shentsize = 0;
351        ehdr->e_shstrndx = 0;
352    }
353
354    /* The program adjusts the file size of any segment that was
355     * truncated. The case of a segment being completely stripped out
356     * is handled separately.
357     */
358    for (i = 0, phdr = phdrs ; i < ehdr->e_phnum ; ++i, ++phdr) {
359        if (phdr->p_offset >= newsize) {
360            phdr->p_offset = newsize;
361            phdr->p_filesz = 0;
362        } else if (phdr->p_offset + phdr->p_filesz > newsize) {
363            phdr->p_filesz = newsize - phdr->p_offset;
364        }
365    }
366
367    return TRUE;
368}
369
370/* commitchanges() writes the new headers back to the original file
371 * and sets the file to its new size.
372 */
373static int commitchanges_32(int fd, Elf32_Ehdr const *ehdr, Elf32_Phdr *phdrs,
374                            unsigned long newsize)
375{
376    size_t      n;
377
378    /* Save the changes to the ELF header, if any.
379     */
380    if (lseek(fd, 0, SEEK_SET))
381        return ferr("could not rewind file");
382    errno = 0;
383    if (write(fd, ehdr, sizeof *ehdr) != sizeof *ehdr)
384        return err("could not modify file");
385
386    /* Save the changes to the program segment header table, if any.
387     */
388    if (lseek(fd, ehdr->e_phoff, SEEK_SET) == (off_t)-1) {
389        err("could not seek in file.");
390        goto warning;
391    }
392    n = ehdr->e_phnum * sizeof *phdrs;
393    if (write(fd, phdrs, n) != (ssize_t)n) {
394        err("could not write to file");
395        goto warning;
396    }
397
398    /* Eleventh-hour sanity check: don't truncate before the end of
399     * the program segment header table.
400     */
401    if (newsize < ehdr->e_phoff + n)
402        newsize = ehdr->e_phoff + n;
403
404    /* Chop off the end of the file.
405     */
406    if (ftruncate(fd, newsize)) {
407        err("could not resize file");
408        goto warning;
409    }
410
411    return TRUE;
412
413  warning:
414    return err("ELF file may have been corrupted!");
415}
416
417static int commitchanges_64(int fd, Elf64_Ehdr const *ehdr, Elf64_Phdr *phdrs,
418                            unsigned long newsize)
419{
420    size_t      n;
421
422    /* Save the changes to the ELF header, if any.
423     */
424    if (lseek(fd, 0, SEEK_SET))
425        return ferr("could not rewind file");
426    errno = 0;
427    if (write(fd, ehdr, sizeof *ehdr) != sizeof *ehdr)
428        return err("could not modify file");
429
430    /* Save the changes to the program segment header table, if any.
431     */
432    if (lseek(fd, ehdr->e_phoff, SEEK_SET) == (off_t)-1) {
433        err("could not seek in file.");
434        goto warning;
435    }
436    n = ehdr->e_phnum * sizeof *phdrs;
437    if (write(fd, phdrs, n) != (ssize_t)n) {
438        err("could not write to file");
439        goto warning;
440    }
441
442    /* Eleventh-hour sanity check: don't truncate before the end of
443     * the program segment header table.
444     */
445    if (newsize < ehdr->e_phoff + n)
446        newsize = ehdr->e_phoff + n;
447
448    /* Chop off the end of the file.
449     */
450    if (ftruncate(fd, newsize)) {
451        err("could not resize file");
452        goto warning;
453    }
454
455    return TRUE;
456
457  warning:
458    return err("ELF file may have been corrupted!");
459}
460
461/* main() loops over the cmdline arguments, leaving all the real work
462 * to the other functions.
463 */
464int main(int argc, char *argv[])
465{
466    int                 fd;
467    int                 is_32bit_elf;
468    Elf32_Ehdr          ehdr32;
469    Elf32_Phdr         *phdrs32 = NULL;
470    Elf64_Ehdr          ehdr64;
471    Elf64_Phdr         *phdrs64 = NULL;
472    unsigned long       newsize;
473    char              **arg;
474    int                 failures = 0;
475
476    if (argc < 2 || argv[1][0] == '-') {
477        printf("Usage: sstrip FILE...\n"
478               "sstrip discards all nonessential bytes from an executable.\n\n"
479               "Version 2.0 Copyright (C) 2000,2001 Brian Raiter.\n"
480               "This program is free software, licensed under the GNU\n"
481               "General Public License. There is absolutely no warranty.\n");
482        return EXIT_SUCCESS;
483    }
484
485    progname = argv[0];
486
487    for (arg = argv + 1 ; *arg != NULL ; ++arg) {
488        filename = *arg;
489
490        fd = open(*arg, O_RDWR);
491        if (fd < 0) {
492            ferr("can't open");
493            ++failures;
494            continue;
495        }
496
497        if (readelfheader_32(fd, &ehdr32)) {
498          is_32bit_elf = TRUE;
499        } 
500        else if (readelfheader_64(fd, &ehdr64)) {
501          is_32bit_elf = FALSE;
502        }
503        else {
504          close(fd);
505          return EXIT_FAILURE;
506        }
507
508        if (is_32bit_elf) {
509          if (!(readphdrtable_32(fd, &ehdr32, &phdrs32)         &&
510              getmemorysize_32(&ehdr32, phdrs32, &newsize)      &&
511              truncatezeros(fd, &newsize)                       &&
512              modifyheaders_32(&ehdr32, phdrs32, newsize)       &&
513              commitchanges_32(fd, &ehdr32, phdrs32, newsize)))
514            ++failures;
515        } 
516        else {
517          if (!(readphdrtable_64(fd, &ehdr64, &phdrs64)         &&
518              getmemorysize_64(&ehdr64, phdrs64, &newsize)      &&
519              truncatezeros(fd, &newsize)                       &&
520              modifyheaders_64(&ehdr64, phdrs64, newsize)       &&
521              commitchanges_64(fd, &ehdr64, phdrs64, newsize)))
522            ++failures;
523        }
524
525        close(fd);
526    }
527
528    return failures ? EXIT_FAILURE : EXIT_SUCCESS;
529}
530
531#else
532
533int main()
534{
535  return (EXIT_SUCCESS);
536}
537
538#endif
Note: See TracBrowser for help on using the repository browser.