source: trunk/src/sstrip.c@ 480

Last change on this file since 480 was 454, checked in by katerina, 10 years ago

Fix for ticket #355 (use calloc instead of malloc).

File size: 13.8 KB
RevLine 
[1]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
[48]5/* Modified for portability and 64bit/32bit elf executables, Rainer Wichmann */
6
7#include "config.h"
[1]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__) && \
[48]18 (defined(HAVE_ELF_H) || defined(HAVE_LINUX_ELF_H)) && \
19 (defined(__linux__) || defined(__FreeBSD__)) && \
20 (defined(__i386__) || defined(__i386) || defined(i386))
[1]21
22/* || defined(__sun) || defined(__sun__) || defined(sun) */
23
[48]24
25#if defined(HAVE_ELF_H)
26#include <elf.h>
27#else
[1]28#include <linux/elf.h>
29#endif
30
31#ifndef TRUE
32#define TRUE 1
33#define FALSE 0
34#endif
35
[48]36#ifndef ELFCLASS32
37#define ELFCLASS32 1 /* 32-bit objects */
[1]38#endif
[48]39#ifndef ELFCLASS64
40#define ELFCLASS64 2 /* 64-bit objects */
41#endif
[1]42
[48]43
44
[1]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 */
[48]72static int readelfheader_32(int fd, Elf32_Ehdr *ehdr)
[1]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 */
[48]88#ifdef ELF_DATA
[1]89 if (ehdr->e_ident[EI_DATA] != ELF_DATA)
90 return err("ELF file has different endianness.");
[48]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)
[1]145 return err("ELF file has different word size.");
146
147 /* Check the target architecture.
148 */
[48]149#ifdef ELF_ARCH
[1]150 if (ehdr->e_machine != ELF_ARCH)
151 return err("ELF file created for different architecture.");
[48]152#endif
[1]153
154 /* Verify the sizes of the ELF header and the program segment
155 * header table entries.
156 */
[48]157 if (ehdr->e_ehsize != sizeof(Elf64_Ehdr))
[1]158 return err("unrecognized ELF header size.");
[48]159 if (ehdr->e_phentsize != sizeof(Elf64_Phdr))
[1]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 */
[48]172static int readphdrtable_32(int fd, Elf32_Ehdr const *ehdr, Elf32_Phdr **phdrs)
[1]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;
[454]180 if (!(*phdrs = calloc(1,size)))
[1]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
[48]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;
[454]198 if (!(*phdrs = calloc(1,size)))
[48]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
[1]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 */
[48]213static int getmemorysize_32(Elf32_Ehdr const *ehdr, Elf32_Phdr const *phdrs,
214 unsigned long *newsize)
[1]215{
[48]216 Elf32_Phdr const *phdr;
[1]217 unsigned long size, n;
[286]218 unsigned int i;
[1]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
[48]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;
[286]247 unsigned int i;
[48]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
[1]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 */
[48]306static int modifyheaders_32(Elf32_Ehdr *ehdr, Elf32_Phdr *phdrs,
307 unsigned long newsize)
[1]308{
[48]309 Elf32_Phdr *phdr;
[286]310 unsigned int i;
[1]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
[48]338static int modifyheaders_64(Elf64_Ehdr *ehdr, Elf64_Phdr *phdrs,
339 unsigned long newsize)
340{
341 Elf64_Phdr *phdr;
[286]342 unsigned int i;
[48]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
[1]370/* commitchanges() writes the new headers back to the original file
371 * and sets the file to its new size.
372 */
[48]373static int commitchanges_32(int fd, Elf32_Ehdr const *ehdr, Elf32_Phdr *phdrs,
374 unsigned long newsize)
[1]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
[48]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
[1]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;
[48]467 int is_32bit_elf;
468 Elf32_Ehdr ehdr32;
469 Elf32_Phdr *phdrs32 = NULL;
470 Elf64_Ehdr ehdr64;
471 Elf64_Phdr *phdrs64 = NULL;
[1]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
[48]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)))
[1]514 ++failures;
[48]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 }
[1]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.