mirror of
https://gitlab.com/padavan-ng/padavan-ng.git
synced 2024-02-13 08:33:30 +08:00
317 lines
8.1 KiB
C
317 lines
8.1 KiB
C
/* sstrip: Copyright (C) 1999,2011 by Brian Raiter <breadbox@muppetlabs.com>
|
|
* License GPLv2+: GNU GPL version 2 or later.
|
|
* This is free software; you are free to change and redistribute it.
|
|
* There is NO WARRANTY, to the extent permitted by law.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#include <elf.h>
|
|
|
|
#include "elfrw.h"
|
|
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
/* The online help text.
|
|
*/
|
|
static char const *yowzitch =
|
|
"Usage: sstrip [OPTIONS] FILE...\n"
|
|
"Remove all nonessential bytes from executable ELF files.\n\n"
|
|
" -z, --zeroes Also discard trailing zero bytes.\n"
|
|
" --help Display this help and exit.\n"
|
|
" --version Display version information and exit.\n";
|
|
|
|
/* Version and license information.
|
|
*/
|
|
static char const *vourzhon =
|
|
"sstrip, version 2.1\n"
|
|
"Copyright (C) 1999,2011 by Brian Raiter <breadbox@muppetlabs.com>\n"
|
|
"License GPLv2+: GNU GPL version 2 or later.\n"
|
|
"This is free software; you are free to change and redistribute it.\n"
|
|
"There is NO WARRANTY, to the extent permitted by law.\n";
|
|
|
|
/* The name of the program.
|
|
*/
|
|
static char const *theprogram;
|
|
|
|
/* TRUE if we should attempt to truncate zero bytes from the end of
|
|
* the file.
|
|
*/
|
|
static int dozerotrunc = FALSE;
|
|
|
|
/* Information for each executable operated upon.
|
|
*/
|
|
static char const *thefilename; /* the name of the current file */
|
|
static FILE *thefile; /* the currently open file handle */
|
|
static Elf64_Ehdr ehdr; /* the current file's ELF header */
|
|
static Elf64_Phdr *phdrs; /* the program segment header table */
|
|
unsigned long newsize; /* the proposed new file size */
|
|
|
|
/* A simple error-handling function. FALSE is always returned for the
|
|
* convenience of the caller.
|
|
*/
|
|
static int err(char const *errmsg)
|
|
{
|
|
fprintf(stderr, "%s: %s: %s\n", theprogram, thefilename, errmsg);
|
|
return FALSE;
|
|
}
|
|
|
|
/* A macro for I/O errors: The given error message is used only when
|
|
* errno is not set.
|
|
*/
|
|
#define ferr(msg) (err(ferror(thefile) ? strerror(errno) : (msg)))
|
|
|
|
/* readcmdline() attemps to parse the command line arguments, and only
|
|
* returns if succeeded and there is work to do.
|
|
*/
|
|
static void readcmdline(int argc, char *argv[])
|
|
{
|
|
static char const *optstring = "z";
|
|
static struct option const options[] = {
|
|
{ "zeros", no_argument, 0, 'z' },
|
|
{ "zeroes", no_argument, 0, 'z' },
|
|
{ "help", no_argument, 0, 'H' },
|
|
{ "version", no_argument, 0, 'V' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
int n;
|
|
|
|
if (argc == 1) {
|
|
fputs(yowzitch, stdout);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
theprogram = argv[0];
|
|
while ((n = getopt_long(argc, argv, optstring, options, NULL)) != EOF) {
|
|
switch (n) {
|
|
case 'z':
|
|
dozerotrunc = TRUE;
|
|
break;
|
|
case 'H':
|
|
fputs(yowzitch, stdout);
|
|
exit(EXIT_SUCCESS);
|
|
case 'V':
|
|
fputs(vourzhon, stdout);
|
|
exit(EXIT_SUCCESS);
|
|
default:
|
|
fputs("Try --help for more information.\n", stderr);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* readelfheader() reads the ELF header into our global variable, and
|
|
* checks to make sure that this is in fact a file that we should be
|
|
* munging.
|
|
*/
|
|
static int readelfheader(void)
|
|
{
|
|
if (elfrw_read_Ehdr(thefile, &ehdr) != 1)
|
|
return ferr("not a valid ELF file");
|
|
|
|
if (ehdr.e_type != ET_EXEC && ehdr.e_type != ET_DYN)
|
|
return err("not an executable or shared-object library.");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* readphdrtable() loads the program segment header table into memory.
|
|
*/
|
|
static int readphdrtable(void)
|
|
{
|
|
if (!ehdr.e_phoff || !ehdr.e_phnum)
|
|
return err("ELF file has no program header table.");
|
|
|
|
if (!(phdrs = realloc(phdrs, ehdr.e_phnum * sizeof *phdrs)))
|
|
return err("Out of memory!");
|
|
if (elfrw_read_Phdrs(thefile, phdrs, ehdr.e_phnum) != ehdr.e_phnum)
|
|
return ferr("missing or incomplete program segment header table.");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* getmemorysize() determines the offset of the last byte of the file
|
|
* that is referenced by an entry in the program segment header table.
|
|
* (Anything in the file after that point is not used when the program
|
|
* is executing, and thus can be safely discarded.)
|
|
*/
|
|
static int getmemorysize(void)
|
|
{
|
|
unsigned long size, n;
|
|
int i;
|
|
|
|
/* Start by setting the size to include the ELF header and the
|
|
* complete program segment header table.
|
|
*/
|
|
size = ehdr.e_phoff + ehdr.e_phnum * sizeof *phdrs;
|
|
if (size < ehdr.e_ehsize)
|
|
size = ehdr.e_ehsize;
|
|
|
|
/* Then keep extending the size to include whatever data the
|
|
* program segment header table references.
|
|
*/
|
|
for (i = 0 ; i < ehdr.e_phnum ; ++i) {
|
|
if (phdrs[i].p_type != PT_NULL) {
|
|
n = phdrs[i].p_offset + phdrs[i].p_filesz;
|
|
if (n > size)
|
|
size = n;
|
|
}
|
|
}
|
|
|
|
newsize = size;
|
|
return TRUE;
|
|
}
|
|
|
|
/* truncatezeros() examines the bytes at the end of the file's
|
|
* size-to-be, and reduces the size to exclude any trailing zero
|
|
* bytes.
|
|
*/
|
|
static int truncatezeros(void)
|
|
{
|
|
unsigned char contents[1024];
|
|
unsigned long size, n;
|
|
|
|
if (!dozerotrunc)
|
|
return TRUE;
|
|
|
|
size = newsize;
|
|
do {
|
|
n = sizeof contents;
|
|
if (n > size)
|
|
n = size;
|
|
if (fseek(thefile, size - n, SEEK_SET))
|
|
return ferr("cannot seek in file.");
|
|
if (fread(contents, n, 1, thefile) != 1)
|
|
return ferr("cannot read file contents");
|
|
while (n && !contents[--n])
|
|
--size;
|
|
} while (size && !n);
|
|
|
|
/* Sanity check.
|
|
*/
|
|
if (!size)
|
|
return err("ELF file is completely blank!");
|
|
|
|
newsize = size;
|
|
return TRUE;
|
|
}
|
|
|
|
/* modifyheaders() removes references to the section header table if
|
|
* it was stripped, and reduces program header table entries that
|
|
* included truncated bytes at the end of the file.
|
|
*/
|
|
static int modifyheaders(void)
|
|
{
|
|
int i;
|
|
|
|
/* If the section header table is gone, then remove all references
|
|
* to it in the ELF header.
|
|
*/
|
|
if (ehdr.e_shoff >= newsize) {
|
|
ehdr.e_shoff = 0;
|
|
ehdr.e_shnum = 0;
|
|
ehdr.e_shstrndx = 0;
|
|
}
|
|
|
|
/* The program adjusts the file size of any segment that was
|
|
* truncated. The case of a segment being completely stripped out
|
|
* is handled separately.
|
|
*/
|
|
for (i = 0 ; i < ehdr.e_phnum ; ++i) {
|
|
if (phdrs[i].p_offset >= newsize) {
|
|
phdrs[i].p_offset = newsize;
|
|
phdrs[i].p_filesz = 0;
|
|
} else if (phdrs[i].p_offset + phdrs[i].p_filesz > newsize) {
|
|
phdrs[i].p_filesz = newsize - phdrs[i].p_offset;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* commitchanges() writes the new headers back to the original file
|
|
* and sets the file to its new size.
|
|
*/
|
|
static int commitchanges(void)
|
|
{
|
|
size_t n;
|
|
|
|
/* Save the changes to the ELF header, if any.
|
|
*/
|
|
if (fseek(thefile, 0, SEEK_SET))
|
|
return ferr("could not rewind file");
|
|
if (!elfrw_write_Ehdr(thefile, &ehdr))
|
|
return ferr("could not modify file");
|
|
|
|
/* Save the changes to the program segment header table, if any.
|
|
*/
|
|
if (fseek(thefile, ehdr.e_phoff, SEEK_SET)) {
|
|
ferr("could not seek in file");
|
|
goto warning;
|
|
}
|
|
if (elfrw_write_Phdrs(thefile, phdrs, ehdr.e_phnum) != ehdr.e_phnum) {
|
|
ferr("could not write to file");
|
|
goto warning;
|
|
}
|
|
|
|
/* Eleventh-hour sanity check: don't truncate before the end of
|
|
* the program segment header table.
|
|
*/
|
|
n = ehdr.e_phnum * ehdr.e_phentsize;
|
|
if (newsize < ehdr.e_phoff + n)
|
|
newsize = ehdr.e_phoff + n;
|
|
|
|
/* Chop off the end of the file.
|
|
*/
|
|
if (ftruncate(fileno(thefile), newsize)) {
|
|
err(errno ? strerror(errno) : "could not resize file");
|
|
goto warning;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
warning:
|
|
return err("ELF file may have been corrupted!");
|
|
}
|
|
|
|
/* main() loops over the cmdline arguments, leaving all the real work
|
|
* to the other functions.
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int failures = 0;
|
|
int i;
|
|
|
|
readcmdline(argc, argv);
|
|
|
|
for (i = optind ; i < argc ; ++i)
|
|
{
|
|
thefilename = argv[i];
|
|
thefile = fopen(thefilename, "r+");
|
|
if (!thefile) {
|
|
err(strerror(errno));
|
|
++failures;
|
|
continue;
|
|
}
|
|
|
|
if (!(readelfheader() &&
|
|
readphdrtable() &&
|
|
getmemorysize() &&
|
|
truncatezeros() &&
|
|
modifyheaders() &&
|
|
commitchanges()))
|
|
++failures;
|
|
|
|
fclose(thefile);
|
|
}
|
|
|
|
return failures ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
}
|