Renamed src/stub/util/ to src/stub/tools/ .
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
# Makefile for armpe_tester
|
||||
|
||||
MAKEFLAGS += -rR
|
||||
SHELL = /bin/sh
|
||||
|
||||
# update path for our special stub build tools
|
||||
ifneq ($(wildcard $(HOME)/local/bin/bin-upx),)
|
||||
export PATH := $(HOME)/local/bin/bin-upx:$(PATH)
|
||||
endif
|
||||
|
||||
|
||||
all: armpe_tester wtest.exe
|
||||
|
||||
armpe_tester: armpe_tester.c
|
||||
arm-9tdmi-linux-gnu-gcc -Wl,--section-start,.interp=0x1000 -g -Wall -W -o $@ $<
|
||||
|
||||
wtest.exe: armpe_tester.c
|
||||
arm-wince-pe-gcc -Wl,--image-base,0x400000 -s -Wall -W -o $@ $<
|
||||
|
||||
mostlyclean clean distclean maintainer-clean:
|
||||
rm -f *.d *.o *.obj
|
||||
rm -f armpe_tester wtest.exe
|
||||
|
||||
.PHONY: all mostlyclean clean distclean maintainer-clean
|
||||
|
||||
@@ -0,0 +1,354 @@
|
||||
/* armpe_tester.c -- ARM/PE loader/tester for arm linux
|
||||
|
||||
This file is part of the UPX executable compressor.
|
||||
|
||||
Copyright (C) 1996-2006 Markus Franz Xaver Johannes Oberhumer
|
||||
Copyright (C) 1996-2006 Laszlo Molnar
|
||||
Copyright (C) 2000-2006 John F. Reiser
|
||||
All Rights Reserved.
|
||||
|
||||
UPX and the UCL library are free software; you can redistribute them
|
||||
and/or modify them under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; see the file COPYING.
|
||||
If not, write to the Free Software Foundation, Inc.,
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
Markus F.X.J. Oberhumer Laszlo Molnar
|
||||
<mfx@users.sourceforge.net> <ml1050@users.sourceforge.net>
|
||||
|
||||
John F. Reiser
|
||||
<jreiser@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
// arm-9tdmi-linux-gnu-gcc -Wl,--section-start,.interp=0x1000
|
||||
// arm-wince-pe-gcc -Wl,--image-base,0x400000
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef i386
|
||||
# define UPX_MMAP_ADDRESS 0x20000000
|
||||
#else
|
||||
# define UPX_MMAP_ADDRESS 0x10000
|
||||
#endif
|
||||
|
||||
#ifdef linux
|
||||
# include <sys/mman.h>
|
||||
#else
|
||||
void *VirtualAlloc(void *address, unsigned size, unsigned type, unsigned protect);
|
||||
# define MEM_COMMIT 0x1000
|
||||
# define PAGE_EXECUTE_READWRITE 0x0040
|
||||
#endif
|
||||
|
||||
typedef unsigned short LE16;
|
||||
typedef unsigned long LE32;
|
||||
#define __attribute_packed
|
||||
|
||||
struct ddirs_t
|
||||
{
|
||||
LE32 vaddr;
|
||||
LE32 size;
|
||||
}
|
||||
__attribute_packed;
|
||||
|
||||
struct pe_header_t
|
||||
{
|
||||
// 0x0
|
||||
char _[4];
|
||||
LE16 cpu;
|
||||
LE16 objects;
|
||||
char __[12];
|
||||
LE16 opthdrsize;
|
||||
LE16 flags;
|
||||
// optional header
|
||||
char ___[4];
|
||||
LE32 codesize;
|
||||
// 0x20
|
||||
LE32 datasize;
|
||||
LE32 bsssize;
|
||||
LE32 entry;
|
||||
LE32 codebase;
|
||||
// 0x30
|
||||
LE32 database;
|
||||
// nt specific fields
|
||||
LE32 imagebase;
|
||||
LE32 objectalign;
|
||||
LE32 filealign;
|
||||
// 0x40
|
||||
char ____[16];
|
||||
// 0x50
|
||||
LE32 imagesize;
|
||||
LE32 headersize;
|
||||
LE32 chksum;
|
||||
LE16 subsystem;
|
||||
LE16 dllflags;
|
||||
// 0x60
|
||||
char _____[20];
|
||||
// 0x74
|
||||
LE32 ddirsentries;
|
||||
|
||||
struct ddirs_t ddirs[16];
|
||||
}
|
||||
__attribute_packed;
|
||||
|
||||
struct pe_section_t
|
||||
{
|
||||
char name[8];
|
||||
LE32 vsize;
|
||||
LE32 vaddr;
|
||||
LE32 size;
|
||||
LE32 rawdataptr;
|
||||
char _[12];
|
||||
LE32 flags;
|
||||
}
|
||||
__attribute_packed;
|
||||
|
||||
|
||||
struct exe_header_t
|
||||
{
|
||||
LE16 mz;
|
||||
LE16 m512;
|
||||
LE16 p512;
|
||||
char _[18];
|
||||
LE16 relocoffs;
|
||||
char __[34];
|
||||
LE32 nexepos;
|
||||
}
|
||||
__attribute_packed;
|
||||
|
||||
enum {
|
||||
PEDIR_EXPORT = 0,
|
||||
PEDIR_IMPORT = 1,
|
||||
PEDIR_RESOURCE = 2,
|
||||
PEDIR_EXCEPTION = 3,
|
||||
PEDIR_SEC = 4,
|
||||
PEDIR_RELOC = 5,
|
||||
PEDIR_DEBUG = 6,
|
||||
PEDIR_COPYRIGHT = 7,
|
||||
PEDIR_GLOBALPTR = 8,
|
||||
PEDIR_TLS = 9,
|
||||
PEDIR_LOADCONF = 10,
|
||||
PEDIR_BOUNDIM = 11,
|
||||
PEDIR_IAT = 12,
|
||||
PEDIR_DELAYIMP = 13,
|
||||
PEDIR_COMRT = 14
|
||||
};
|
||||
|
||||
#define get_le32(p) (*(unsigned *) (p))
|
||||
#define set_le32(p, v) (*(unsigned *) (p) = (v))
|
||||
#define get_le16(p) (*(unsigned short *) (p))
|
||||
|
||||
static struct pe_header_t ih;
|
||||
static struct pe_section_t isections[3];
|
||||
static FILE *f;
|
||||
static void *vaddr;
|
||||
static FILE *out;
|
||||
|
||||
static int print(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, format);
|
||||
ret = fprintf(out, format, ap);
|
||||
fflush(out);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int load(const char *file)
|
||||
{
|
||||
struct exe_header_t h;
|
||||
int ic;
|
||||
unsigned pe_offset = 0;
|
||||
|
||||
if ((f = fopen(file, "rb")) == NULL)
|
||||
return print("can not open file: %s\n", file);
|
||||
|
||||
for (ic = 0; ic < 20; ic++)
|
||||
{
|
||||
if (fseek(f, pe_offset, SEEK_SET)
|
||||
|| fread(&h, sizeof(h), 1, f) != 1)
|
||||
return print("read error at %u\n", pe_offset);
|
||||
|
||||
if (h.mz == 'M' + 'Z'*256) // dos exe
|
||||
{
|
||||
if (h.relocoffs >= 0x40) // new format exe
|
||||
pe_offset += h.nexepos;
|
||||
else
|
||||
pe_offset += h.p512 * 512 + h.m512 - h.m512 ? 512 : 0;
|
||||
}
|
||||
else if (get_le32(&h) == 'P' + 'E'*256)
|
||||
break;
|
||||
else
|
||||
return print("bad header at %u\n", pe_offset);
|
||||
}
|
||||
if (ic == 20)
|
||||
return print("pe header not found\n");
|
||||
if (fseek(f, pe_offset, SEEK_SET)
|
||||
|| fread(&ih, sizeof(ih), 1, f) != 1)
|
||||
return print("can not load pe header\n");
|
||||
|
||||
if (ih.cpu != 0x1c0 && ih.cpu != 0x1c2)
|
||||
return print("unsupported processor type: %x\n", ih.cpu);
|
||||
|
||||
if (ih.objects != 3 || fread(isections, sizeof(isections), 1, f) != 1)
|
||||
return print("error reading section descriptors\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read(void)
|
||||
{
|
||||
unsigned ic;
|
||||
#ifdef linux
|
||||
vaddr = mmap((void *) UPX_MMAP_ADDRESS, ih.imagesize,
|
||||
PROT_WRITE | PROT_READ | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
if (((int) vaddr) == -1)
|
||||
return print("mmap() failed: %d\n", errno);
|
||||
#else
|
||||
if ((vaddr = VirtualAlloc(0, ih.imagesize,
|
||||
MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == 0)
|
||||
return print("VirtualAlloc() failed\n");
|
||||
print("VirtualAlloc() ok %x\n", vaddr);
|
||||
#endif
|
||||
|
||||
for (ic = 1; ic <= 2; ic++)
|
||||
if (fseek(f, isections[ic].rawdataptr, SEEK_SET)
|
||||
|| fread(vaddr + isections[ic].vaddr,
|
||||
isections[ic].vsize, 1, f) != 1)
|
||||
return print("error reading section %u\n", ic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump(char n)
|
||||
{
|
||||
char buf[100];
|
||||
#ifdef linux
|
||||
snprintf(buf, sizeof(buf), "/tmp/a.dump%c", n);
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "/a.dump%c", n);
|
||||
#endif
|
||||
FILE *f2 = fopen(buf, "wb");
|
||||
fwrite(vaddr + 0x1000, ih.imagesize - 0x1000, 1, f2);
|
||||
fclose(f2);
|
||||
}
|
||||
|
||||
static int loadlibraryw(unsigned short *name)
|
||||
{
|
||||
return name[0] + name[1] * 0x100 + name[2] * 0x10000;
|
||||
}
|
||||
|
||||
static int getprocaddressa(unsigned h, const char *proc)
|
||||
{
|
||||
unsigned p = (unsigned) proc;
|
||||
if (p < 0x10000)
|
||||
{
|
||||
print("getprocaddressa called %c%c%c, ordinal %u\n",
|
||||
h, h >> 8, h >> 16, p);
|
||||
return h + p * 0x10000;
|
||||
}
|
||||
print("getprocaddressa called %c%c%c, name %s\n",
|
||||
h, h >> 8, h >> 16, proc);
|
||||
return h + proc[0] * 0x10000 + proc[1] * 0x1000000;
|
||||
}
|
||||
|
||||
static void cachesync(unsigned v)
|
||||
{
|
||||
print("cachesync called %u\n", v);
|
||||
}
|
||||
|
||||
static int import(void)
|
||||
{
|
||||
if (ih.ddirs[PEDIR_IMPORT].vaddr == 0)
|
||||
return print("no imports?\n");
|
||||
void *imports = vaddr + ih.ddirs[PEDIR_IMPORT].vaddr;
|
||||
void *coredll_imports = vaddr + get_le32(imports + 16);
|
||||
set_le32(coredll_imports, (unsigned) loadlibraryw);
|
||||
set_le32(coredll_imports + 4, (unsigned) getprocaddressa);
|
||||
set_le32(coredll_imports + 8, (unsigned) cachesync);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reloc(void)
|
||||
{
|
||||
if (ih.ddirs[PEDIR_RELOC].vaddr == 0)
|
||||
return 0;
|
||||
void *relocs = vaddr + ih.ddirs[PEDIR_RELOC].vaddr;
|
||||
void *page = vaddr + get_le32(relocs);
|
||||
unsigned size = get_le32(relocs + 4);
|
||||
if (size != ih.ddirs[PEDIR_RELOC].size)
|
||||
return print("only 1 page can be relocated\n");
|
||||
unsigned num = (size - 8) / 2;
|
||||
while (num--)
|
||||
{
|
||||
unsigned pos = get_le16(relocs + 8 + num * 2);
|
||||
if (pos == 0)
|
||||
continue;
|
||||
if ((pos & 0xF000) != 0x3000)
|
||||
return print("unknown relocation type: %x\n", pos);
|
||||
|
||||
void *r = page + (pos & 0xFFF);
|
||||
set_le32(r, get_le32(r) - ih.imagebase + (unsigned) vaddr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump2(int c)
|
||||
{
|
||||
print("dump2 %c\n", c);
|
||||
dump(c);
|
||||
}
|
||||
|
||||
static void call(void)
|
||||
{
|
||||
#ifndef i386
|
||||
void (*entry)(void (*)(int), unsigned) = vaddr + ih.entry;
|
||||
entry(dump2, 1);
|
||||
dump('z');
|
||||
#endif
|
||||
}
|
||||
|
||||
static int main2(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
return print("usage: %s arm_pe_file\n", argv[0]), 1;
|
||||
if (load(argv[1]))
|
||||
return 2;
|
||||
if (read())
|
||||
return 3;
|
||||
dump('0');
|
||||
if (import())
|
||||
return 4;
|
||||
dump('1');
|
||||
if (reloc())
|
||||
return 5;
|
||||
dump('2');
|
||||
|
||||
call();
|
||||
print("ok.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
out = stdout;
|
||||
#ifndef linux
|
||||
out = fopen("/wtest.log", "wt");
|
||||
#endif
|
||||
int ret = main2(argc, argv);
|
||||
fclose(out);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Makefile for sstrip
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
##all: amd64-linux-sstrip
|
||||
##all: i386-linux-sstrip
|
||||
all: sstrip
|
||||
|
||||
sstrip: sstrip.c
|
||||
gcc -O2 -g -Wall -W -o $@ $<
|
||||
amd64-linux-sstrip: sstrip.c
|
||||
gcc -m64 -O2 -g -Wall -W -o $@ $<
|
||||
i386-linux-sstrip: sstrip.c
|
||||
gcc -m32 -O2 -g -Wall -W -o $@ $<
|
||||
|
||||
mostlyclean clean distclean maintainer-clean:
|
||||
rm -f *.d *.o *.obj
|
||||
rm -f sstrip amd64-linux-sstrip i386-linux-sstrip
|
||||
|
||||
.PHONY: all mostlyclean clean distclean maintainer-clean
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
sstrip is a small utility that removes the contents at the end of an
|
||||
ELF file that are not part of the program's memory image.
|
||||
|
||||
Most ELF executables are built with both a program header table and a
|
||||
section header table. However, only the former is required in order
|
||||
for the OS to load, link and execute a program. sstrip attempts to
|
||||
extract the ELF header, the program header table, and its contents,
|
||||
leaving everything else in the bit bucket. It can only remove parts of
|
||||
the file that occur at the end, after the parts to be saved. However,
|
||||
this almost always includes the section header table, and occasionally
|
||||
a few random sections that are not used when running a program.
|
||||
|
||||
It should be noted that the GNU bfd library is (understandably)
|
||||
dependent on the section header table as an index to the file's
|
||||
contents. Thus, an executable file that has no section header table
|
||||
cannot be used with gdb, objdump, or any other program based upon the
|
||||
bfd library, at all. In fact, the program will not even recognize the
|
||||
file as a valid executable. (This limitation is noted in the source
|
||||
code comments for bfd, and is marked "FIXME", so this may change at
|
||||
some future date. However, I would imagine that it is a pretty
|
||||
low-priority item, as executables without a section header table are
|
||||
rare in the extreme.) This probably also explains why strip doesn't
|
||||
offer the option to do this.
|
||||
|
||||
Shared library files may also have their section header table removed.
|
||||
Such a library will still function; however, it will no longer be
|
||||
possible for a compiler to link a new program against it.
|
||||
|
||||
As an added bonus, sstrip also tries to removes trailing zero bytes
|
||||
from the end of the file. (This normally cannot be done with an
|
||||
executable that has a section header table.)
|
||||
|
||||
sstrip is a very simplistic program. It depends upon the common
|
||||
practice of putting the parts of the file that contribute to the
|
||||
memory image at the front, and the remaining material at the end. This
|
||||
permits it to discard the latter material without affecting file
|
||||
offsets and memory addresses in what remains. Of course, the ELF
|
||||
standard permits files to be organized in almost any order, so if a
|
||||
pathological linker decided to put its section headers at the top,
|
||||
sstrip would be useless on such executables.
|
||||
@@ -0,0 +1,73 @@
|
||||
This distribution is a collection of programs that are generally
|
||||
unrelated, except in that they all deal with the ELF file format.
|
||||
|
||||
The main purpose of these programs is to be illustrative and
|
||||
educational -- to help fellow programmers understand the ELF file
|
||||
format and something of how it works under the Linux platform. For the
|
||||
most part, these programs have limited real-world utility. (Although I
|
||||
myself have found some of these programs quite useful while writing
|
||||
the others.)
|
||||
|
||||
Each program is independent. There is no shared code between them, and
|
||||
in fact they all take slightly different approaches to handling ELF
|
||||
files.
|
||||
|
||||
The table of contents:
|
||||
|
||||
sstrip/
|
||||
sstrip is a small utility that removes everything from an ELF file
|
||||
that is not part of the file's memory image.
|
||||
|
||||
rebind/
|
||||
rebind is another small utility that alters the binding of selected
|
||||
exported symbols in an ELF object file.
|
||||
|
||||
elfls/
|
||||
elfls is a utility that displays an ELF file's program and/or
|
||||
section header tables, which serve as a kind of global roadmap to
|
||||
the file's contents.
|
||||
|
||||
elftoc/
|
||||
elftoc takes an ELF file and generates C code that defines a
|
||||
structure with the same memory image, using the structures and
|
||||
preprocessor symbols defined in <linux/elf.h>.
|
||||
|
||||
ebfc/
|
||||
ebfc is a compiler for a tiny programming language. The compiler can
|
||||
generate ELF executables, object files, and shared libraries.
|
||||
|
||||
tiny/
|
||||
This directory contains a collection of very small ELF executables.
|
||||
|
||||
See the README in each directory for more details.
|
||||
|
||||
The ELF standard is necessary reading if you wish to fully understand
|
||||
how all of the programs work. You can download a copy as a Postscript
|
||||
document from ftp://tsx.mit.edu/pub/linux/packages/GCC/ELF.doc.tar.gz.
|
||||
Alternately, you can obtain a flat-text transcription of this document
|
||||
from http://www.muppetlabs.com/~breadbox/software/ELF.txt.
|
||||
|
||||
These programs are Copyright (C) 1999-2001 by Brian Raiter.
|
||||
|
||||
These programs are all free software; you can redistribute and/or
|
||||
modify them under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
These programs are distributed in the hope that they will be
|
||||
interesting, but without any warranty; without even the implied
|
||||
warranty of merchantability or fitness for a particular purpose.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program, in the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
Boston, MA 02111-1307 USA.
|
||||
|
||||
Bug reports and general feedback should be directed to the author at
|
||||
breadbox@muppetlabs.com.
|
||||
|
||||
Share and enjoy.
|
||||
|
||||
Brian Raiter
|
||||
breadbox@muppetlabs.com
|
||||
@@ -0,0 +1,473 @@
|
||||
/* http://www.muppetlabs.com/~breadbox/software/elfkickers.html */
|
||||
|
||||
/* sstrip: Copyright (C) 1999-2001 by Brian Raiter, under the GNU
|
||||
* General Public License. No warranty. See COPYING for details.
|
||||
*
|
||||
* Aug 23, 2004 Hacked by Manuel Novoa III <mjn3@codepoet.org> to
|
||||
* handle targets of different endianness and/or elf class, making
|
||||
* it more useful in a cross-devel environment.
|
||||
*/
|
||||
|
||||
/* ============== original README ===================
|
||||
*
|
||||
* sstrip is a small utility that removes the contents at the end of an
|
||||
* ELF file that are not part of the program's memory image.
|
||||
*
|
||||
* Most ELF executables are built with both a program header table and a
|
||||
* section header table. However, only the former is required in order
|
||||
* for the OS to load, link and execute a program. sstrip attempts to
|
||||
* extract the ELF header, the program header table, and its contents,
|
||||
* leaving everything else in the bit bucket. It can only remove parts of
|
||||
* the file that occur at the end, after the parts to be saved. However,
|
||||
* this almost always includes the section header table, and occasionally
|
||||
* a few random sections that are not used when running a program.
|
||||
*
|
||||
* It should be noted that the GNU bfd library is (understandably)
|
||||
* dependent on the section header table as an index to the file's
|
||||
* contents. Thus, an executable file that has no section header table
|
||||
* cannot be used with gdb, objdump, or any other program based upon the
|
||||
* bfd library, at all. In fact, the program will not even recognize the
|
||||
* file as a valid executable. (This limitation is noted in the source
|
||||
* code comments for bfd, and is marked "FIXME", so this may change at
|
||||
* some future date. However, I would imagine that it is a pretty
|
||||
* low-priority item, as executables without a section header table are
|
||||
* rare in the extreme.) This probably also explains why strip doesn't
|
||||
* offer the option to do this.
|
||||
*
|
||||
* Shared library files may also have their section header table removed.
|
||||
* Such a library will still function; however, it will no longer be
|
||||
* possible for a compiler to link a new program against it.
|
||||
*
|
||||
* As an added bonus, sstrip also tries to removes trailing zero bytes
|
||||
* from the end of the file. (This normally cannot be done with an
|
||||
* executable that has a section header table.)
|
||||
*
|
||||
* sstrip is a very simplistic program. It depends upon the common
|
||||
* practice of putting the parts of the file that contribute to the
|
||||
* memory image at the front, and the remaining material at the end. This
|
||||
* permits it to discard the latter material without affecting file
|
||||
* offsets and memory addresses in what remains. Of course, the ELF
|
||||
* standard permits files to be organized in almost any order, so if a
|
||||
* pathological linker decided to put its section headers at the top,
|
||||
* sstrip would be useless on such executables.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <elf.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
/* The name of the program.
|
||||
*/
|
||||
static char const *progname;
|
||||
|
||||
/* The name of the current file.
|
||||
*/
|
||||
static char const *filename;
|
||||
|
||||
|
||||
/* 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", progname, filename, errmsg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* A flag to signal the need for endian reversal.
|
||||
*/
|
||||
static int do_reverse_endian;
|
||||
|
||||
/* Get a value from the elf header, compensating for endianness.
|
||||
*/
|
||||
#define EGET(X) \
|
||||
(__extension__ ({ \
|
||||
uint64_t __res; \
|
||||
if (!do_reverse_endian) { \
|
||||
__res = (X); \
|
||||
} else if (sizeof(X) == 1) { \
|
||||
__res = (X); \
|
||||
} else if (sizeof(X) == 2) { \
|
||||
__res = bswap_16((X)); \
|
||||
} else if (sizeof(X) == 4) { \
|
||||
__res = bswap_32((X)); \
|
||||
} else if (sizeof(X) == 8) { \
|
||||
__res = bswap_64((X)); \
|
||||
} else { \
|
||||
fprintf(stderr, "%s: %s: EGET failed for size %ld\n", \
|
||||
progname, filename, (long) sizeof(X)); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
__res; \
|
||||
}))
|
||||
|
||||
/* Set a value 'Y' in the elf header to 'X', compensating for endianness.
|
||||
*/
|
||||
#define ESET(Y,X) \
|
||||
do if (!do_reverse_endian) { \
|
||||
Y = (X); \
|
||||
} else if (sizeof(Y) == 1) { \
|
||||
Y = (X); \
|
||||
} else if (sizeof(Y) == 2) { \
|
||||
Y = bswap_16((uint16_t)(X)); \
|
||||
} else if (sizeof(Y) == 4) { \
|
||||
Y = bswap_32((uint32_t)(X)); \
|
||||
} else if (sizeof(Y) == 8) { \
|
||||
Y = bswap_64((uint64_t)(X)); \
|
||||
} else { \
|
||||
fprintf(stderr, "%s: %s: ESET failed for size %ld\n", \
|
||||
progname, filename, (long) sizeof(Y)); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* A macro for I/O errors: The given error message is used only when
|
||||
* errno is not set.
|
||||
*/
|
||||
#define ferr(msg) (err(errno ? strerror(errno) : (msg)))
|
||||
|
||||
|
||||
|
||||
#define HEADER_FUNCTIONS(CLASS) \
|
||||
\
|
||||
/* 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 ## CLASS (int fd, Elf ## CLASS ## _Ehdr *ehdr) \
|
||||
{ \
|
||||
if (read(fd, ((char *)ehdr)+EI_NIDENT, sizeof(*ehdr) - EI_NIDENT) \
|
||||
!= (ssize_t)sizeof(*ehdr) - EI_NIDENT) \
|
||||
return ferr("missing or incomplete ELF header."); \
|
||||
\
|
||||
/* Verify the sizes of the ELF header and the program segment \
|
||||
* header table entries. \
|
||||
*/ \
|
||||
if (EGET(ehdr->e_ehsize) != sizeof(Elf ## CLASS ## _Ehdr)) \
|
||||
return err("unrecognized ELF header size."); \
|
||||
if (EGET(ehdr->e_phentsize) != sizeof(Elf ## CLASS ## _Phdr)) \
|
||||
return err("unrecognized program segment header size."); \
|
||||
\
|
||||
/* Finally, check the file type. \
|
||||
*/ \
|
||||
if (EGET(ehdr->e_type) != ET_EXEC && EGET(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 ## CLASS (int fd, Elf ## CLASS ## _Ehdr const *ehdr, \
|
||||
Elf ## CLASS ## _Phdr **phdrs) \
|
||||
{ \
|
||||
size_t size; \
|
||||
\
|
||||
if (!EGET(ehdr->e_phoff) || !EGET(ehdr->e_phnum) \
|
||||
) return err("ELF file has no program header table."); \
|
||||
\
|
||||
size = EGET(ehdr->e_phnum) * sizeof **phdrs; \
|
||||
if (!(*phdrs = malloc(size))) \
|
||||
return err("Out of memory!"); \
|
||||
\
|
||||
errno = 0; \
|
||||
if (read(fd, *phdrs, size) != (ssize_t)size) \
|
||||
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 ## CLASS (Elf ## CLASS ## _Ehdr const *ehdr, \
|
||||
Elf ## CLASS ## _Phdr const *phdrs, \
|
||||
unsigned long *newsize) \
|
||||
{ \
|
||||
Elf ## CLASS ## _Phdr const *phdr; \
|
||||
unsigned long size, n; \
|
||||
size_t i; \
|
||||
\
|
||||
/* Start by setting the size to include the ELF header and the \
|
||||
* complete program segment header table. \
|
||||
*/ \
|
||||
size = EGET(ehdr->e_phoff) + EGET(ehdr->e_phnum) * sizeof *phdrs; \
|
||||
if (size < sizeof *ehdr) \
|
||||
size = sizeof *ehdr; \
|
||||
\
|
||||
/* Then keep extending the size to include whatever data the \
|
||||
* program segment header table references. \
|
||||
*/ \
|
||||
for (i = 0, phdr = phdrs ; i < EGET(ehdr->e_phnum) ; ++i, ++phdr) { \
|
||||
if (EGET(phdr->p_type) != PT_NULL) { \
|
||||
n = EGET(phdr->p_offset) + EGET(phdr->p_filesz); \
|
||||
if (n > size) \
|
||||
size = n; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
*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 ## CLASS (Elf ## CLASS ## _Ehdr *ehdr, \
|
||||
Elf ## CLASS ## _Phdr *phdrs, \
|
||||
unsigned long newsize) \
|
||||
{ \
|
||||
Elf ## CLASS ## _Phdr *phdr; \
|
||||
size_t i; \
|
||||
\
|
||||
/* If the section header table is gone, then remove all references \
|
||||
* to it in the ELF header. \
|
||||
*/ \
|
||||
if (EGET(ehdr->e_shoff) >= newsize) { \
|
||||
ESET(ehdr->e_shoff,0); \
|
||||
ESET(ehdr->e_shnum,0); \
|
||||
ESET(ehdr->e_shentsize,0); \
|
||||
ESET(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, phdr = phdrs ; i < EGET(ehdr->e_phnum) ; ++i, ++phdr) { \
|
||||
if (EGET(phdr->p_offset) >= newsize) { \
|
||||
ESET(phdr->p_offset,newsize); \
|
||||
ESET(phdr->p_filesz,0); \
|
||||
} else if (EGET(phdr->p_offset) + EGET(phdr->p_filesz) > newsize) { \
|
||||
newsize -= EGET(phdr->p_offset); \
|
||||
ESET(phdr->p_filesz, newsize); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return TRUE; \
|
||||
} \
|
||||
\
|
||||
/* commitchanges() writes the new headers back to the original file \
|
||||
* and sets the file to its new size. \
|
||||
*/ \
|
||||
static int commitchanges ## CLASS (int fd, Elf ## CLASS ## _Ehdr const *ehdr, \
|
||||
Elf ## CLASS ## _Phdr *phdrs, \
|
||||
unsigned long newsize) \
|
||||
{ \
|
||||
size_t n; \
|
||||
\
|
||||
/* Save the changes to the ELF header, if any. \
|
||||
*/ \
|
||||
if (lseek(fd, 0, SEEK_SET)) \
|
||||
return ferr("could not rewind file"); \
|
||||
errno = 0; \
|
||||
if (write(fd, ehdr, sizeof *ehdr) != (ssize_t)sizeof *ehdr) \
|
||||
return err("could not modify file"); \
|
||||
\
|
||||
/* Save the changes to the program segment header table, if any. \
|
||||
*/ \
|
||||
if (lseek(fd, EGET(ehdr->e_phoff), SEEK_SET) == (off_t)-1) { \
|
||||
err("could not seek in file."); \
|
||||
goto warning; \
|
||||
} \
|
||||
n = EGET(ehdr->e_phnum) * sizeof *phdrs; \
|
||||
if (write(fd, phdrs, n) != (ssize_t)n) { \
|
||||
err("could not write to file"); \
|
||||
goto warning; \
|
||||
} \
|
||||
\
|
||||
/* Eleventh-hour sanity check: don't truncate before the end of \
|
||||
* the program segment header table. \
|
||||
*/ \
|
||||
if (newsize < EGET(ehdr->e_phoff) + n) \
|
||||
newsize = EGET(ehdr->e_phoff) + n; \
|
||||
\
|
||||
/* Chop off the end of the file. \
|
||||
*/ \
|
||||
if (ftruncate(fd, newsize)) { \
|
||||
err("could not resize file"); \
|
||||
goto warning; \
|
||||
} \
|
||||
\
|
||||
return TRUE; \
|
||||
\
|
||||
warning: \
|
||||
return err("ELF file may have been corrupted!"); \
|
||||
}
|
||||
|
||||
|
||||
/* First elements of Elf32_Ehdr and Elf64_Ehdr are common.
|
||||
*/
|
||||
static int readelfheaderident(int fd, Elf32_Ehdr *ehdr)
|
||||
{
|
||||
errno = 0;
|
||||
if (read(fd, ehdr, EI_NIDENT) != EI_NIDENT)
|
||||
return ferr("missing or incomplete ELF header.");
|
||||
|
||||
/* Check the ELF signature.
|
||||
*/
|
||||
if (!(ehdr->e_ident[EI_MAG0] == ELFMAG0 &&
|
||||
ehdr->e_ident[EI_MAG1] == ELFMAG1 &&
|
||||
ehdr->e_ident[EI_MAG2] == ELFMAG2 &&
|
||||
ehdr->e_ident[EI_MAG3] == ELFMAG3))
|
||||
{
|
||||
err("missing ELF signature.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Compare the file's class and endianness with the program's.
|
||||
*/
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) {
|
||||
do_reverse_endian = 0;
|
||||
} else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) {
|
||||
/* fprintf(stderr, "ELF file has different endianness.\n"); */
|
||||
do_reverse_endian = 1;
|
||||
}
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) {
|
||||
/* fprintf(stderr, "ELF file has different endianness.\n"); */
|
||||
do_reverse_endian = 1;
|
||||
} else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) {
|
||||
do_reverse_endian = 0;
|
||||
}
|
||||
#else
|
||||
#error unkown endianness
|
||||
#endif
|
||||
else {
|
||||
err("Unsupported endianness");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check the target architecture.
|
||||
*/
|
||||
/* if (EGET(ehdr->e_machine) != ELF_ARCH) { */
|
||||
/* /\* return err("ELF file created for different architecture."); *\/ */
|
||||
/* fprintf(stderr, "ELF file created for different architecture.\n"); */
|
||||
/* } */
|
||||
return ehdr->e_ident[EI_CLASS];
|
||||
}
|
||||
|
||||
|
||||
HEADER_FUNCTIONS(32)
|
||||
|
||||
HEADER_FUNCTIONS(64)
|
||||
|
||||
/* 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(int fd, unsigned long *newsize)
|
||||
{
|
||||
unsigned char contents[1024];
|
||||
unsigned long size, n;
|
||||
|
||||
size = *newsize;
|
||||
do {
|
||||
n = sizeof contents;
|
||||
if (n > size)
|
||||
n = size;
|
||||
if (lseek(fd, size - n, SEEK_SET) == (off_t)-1)
|
||||
return ferr("cannot seek in file.");
|
||||
if (read(fd, contents, n) != (ssize_t)n)
|
||||
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;
|
||||
}
|
||||
|
||||
/* main() loops over the cmdline arguments, leaving all the real work
|
||||
* to the other functions.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fd;
|
||||
union {
|
||||
Elf32_Ehdr ehdr32;
|
||||
Elf64_Ehdr ehdr64;
|
||||
} e;
|
||||
union {
|
||||
Elf32_Phdr *phdrs32;
|
||||
Elf64_Phdr *phdrs64;
|
||||
} p;
|
||||
unsigned long newsize;
|
||||
char **arg;
|
||||
int failures = 0;
|
||||
|
||||
if (argc < 2 || argv[1][0] == '-') {
|
||||
printf("Usage: sstrip FILE...\n"
|
||||
"sstrip discards all nonessential bytes from an executable.\n\n"
|
||||
"Version 2.0-X Copyright (C) 2000,2001 Brian Raiter.\n"
|
||||
"Cross-devel hacks Copyright (C) 2004 Manuel Novoa III.\n"
|
||||
"This program is free software, licensed under the GNU\n"
|
||||
"General Public License. There is absolutely no warranty.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
progname = argv[0];
|
||||
|
||||
for (arg = argv + 1 ; *arg != NULL ; ++arg) {
|
||||
filename = *arg;
|
||||
|
||||
fd = open(*arg, O_RDWR);
|
||||
if (fd < 0) {
|
||||
ferr("can't open");
|
||||
++failures;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (readelfheaderident(fd, &e.ehdr32)) {
|
||||
case ELFCLASS32:
|
||||
if (!(readelfheader32(fd, &e.ehdr32) &&
|
||||
readphdrtable32(fd, &e.ehdr32, &p.phdrs32) &&
|
||||
getmemorysize32(&e.ehdr32, p.phdrs32, &newsize) &&
|
||||
truncatezeros(fd, &newsize) &&
|
||||
modifyheaders32(&e.ehdr32, p.phdrs32, newsize) &&
|
||||
commitchanges32(fd, &e.ehdr32, p.phdrs32, newsize)))
|
||||
++failures;
|
||||
break;
|
||||
case ELFCLASS64:
|
||||
if (!(readelfheader64(fd, &e.ehdr64) &&
|
||||
readphdrtable64(fd, &e.ehdr64, &p.phdrs64) &&
|
||||
getmemorysize64(&e.ehdr64, p.phdrs64, &newsize) &&
|
||||
truncatezeros(fd, &newsize) &&
|
||||
modifyheaders64(&e.ehdr64, p.phdrs64, newsize) &&
|
||||
commitchanges64(fd, &e.ehdr64, p.phdrs64, newsize)))
|
||||
++failures;
|
||||
break;
|
||||
default:
|
||||
++failures;
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return failures ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
vi:ts=4:et:nowrap
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user