About Introducing Your Own Code

 

 

by SiuL+Hacky

+cracker

Courtesy of Fravia's page of reverse engineering

 
 

hi +fravia, it's curious, linux surprises myself. Two or three essays ago i was convinced they would be the last ones commenting general facts. I was thinking that new articles would be just explanations of some interesting protection scheme, but nothing else. Well, my friend there are lot of things left :-).
And think that protectors have not started to think, this is just a kid playing with the system.
Hope it'll like you,
SiuL+Hacky

ps. by the way, have you ever think of how incredibly well protected is the whole fucking machine outthere. The problem is that you need a good deal of technical background or some kind of Richard Feynman close to you :-). It's a good job we have not lost our chance to learn !  

 
 

There is a crack, a crack in everything That's how the light gets in

 

Rating

( )Beginner ( )Intermediate (X)Advanced ( )Expert

 



About Introducing Your Own Code
Written by SiuL+Hacky

Introduction

I must admit one of the funny ways to crack applications is using dlls from the program; it is what we've called Object Oriented Cracking. In my opinion that's one of the more powerful and witty approaches i've seen. Despite this fact, i faced in my last windows-cracking-times, the problem of calling routines that were not present in dlls, but executables.

The general problem is: imagine a program that validates some code. As a matter of fact, what the interesting routine does is not important, but think about you have to modify the execution flow in order to test different things. Sometimes you can just patch the code, but many times the amount of foreign code you must enter is larger that the code you can fit into "dead areas". You can always, of course, overwrite code, but i always thought that's not a quite elegant approach. One example is a validation routine that it's not easy to clone, because some kind of data is initialized, and you have to call just in the right moment, neither before nor after. Now i want to make a brute force attack and i need to give room for my code that generates new alternatives, test them, and show me somehow that i've been successful.

Tools required

In this essay you'll need no particular tool that is not available in any distribution: gcc, ld-so, binutils ... You may need for aditional information the source code of some programs. If it does not come with your distribution, at least Debian and RedHat have source codes available in their respective ftp sites and mirrors.

Target's URL/FTP

In this essay i'm gonna talk about general technics and ideas that are not applied to any particular target.

Essay

Now we're gonna see different ways to do it. Some of them proved to be impossible, others were rather complicated and others were finally good. You'll see if some of them could deserve further development, and it would be happy if someone can do useful things with options i refused (or even better, new ones).

1. MODIFYING ELF HEADERS

The first idea was to simply ADD the code. As i briefly explained in my very first linux essay, linux uses mainly ELF format executables, whose structure is really modular, so one could think about just appending your code to some part of the executable. There two documents about ELF format (that i know of), that will answer many of the question that can arise:

    elf.hps -> Format gory details
    elf.ps.gz -> Programmers guide

Firstly a small review about ELF format. Three elements are important:

Program Header:
    PHDR off    0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2
         filesz 0x000000a0 memsz 0x000000a0 flags r-x
  INTERP off    0x000000d4 vaddr 0x080480d4 paddr 0x080480d4 align 2**0
         filesz 0x00000013 memsz 0x00000013 flags r--
    LOAD off    0x00000000 vaddr 0x08048000 paddr 0x08048000
align 2**12
         filesz 0x0000055c memsz 0x0000055c flags r-x
    LOAD off    0x0000055c vaddr 0x0804955c paddr 0x0804955c align 2**12
         filesz 0x000000d4 memsz 0x000000d8 flags rw-
 DYNAMIC off    0x00000598 vaddr 0x08049598 paddr 0x08049598 align 2**2
         filesz 0x00000098 memsz 0x00000098 flags rw-

The code segment is present in the third element. Note that alignment is 4096, the size of each page of virtual memory.

This is just a reminding if someone have already read about elf structure, i'm not teaching elf structure. If you're lazy, as me, you will not want to read the docs i pointed before, but do not expect to understand the what i'm gonna discuss now.

Now let's suppose i have a piece of code i want to add to the program. I could think about creating a new code section and append it. Ok, that's not complicated, as you can do it with a simple program named objcopy. The program comes with the binutils package, that every distribution carries. One of the features of objcopy is to add a section given a file name.

That sounds good, let's suppose i program my new code in C, i compile it (i can link it statically to ease the task), and then objcopy glues it to the protected crap. Well it works, but ... objcopy does nothing to map the new section onto the executable segment, where all the code is loaded. In other words my new code is unreachable, because it is never loaded.

A pity, but now you'll begin to realize the difficulties of the job, you must edit by yourself the elf headers and try to make your new section reachable from the rest of the code. Our code is not located physically contiguous to the rest of the executable code:

------------ elf headers and so on ------------
------------ data from different kinds ------------
------------ native code ------------
------------ more data/code ------------
------------ our code ------------

I'm locating the new section as the last one, not only because it's the easiest solution, but it's the way objcopy does. The problem here is that we can't overlap segments (nor sections), so due to the fact you can't (AFAIK) define two code segments, you'll get nothing. The only thing you could do is expanding the current code section (.text section) and append at the end the new piece of code. Of course you'll have to expand the segment too, and other things i'll tell you soon (that makes the task a nightmare).

But before tackling the problems of expanding a .text section, let's face the problem of modifying elf files. Objcopy can handle section attributes and so on, but do not even think about modifying attributes from program headers:


Now if somebody's interested on writing X-Windows apps (with some of the new and attractive graphic libraries for instance), a VERY USEFUL tool could be to program a graphical ELF editor, that could have as a "maximum" challenge: insert data/code into sections. If anybody has the guts to try it i could help him with ELF details (if necessary, of course).


Ok, unless you could rebuild completely an executable with BFD, you'll need modifications in the elf header in order to expand .text section. I think there are two different groups of modifications you have to do, one more obvious, and the second more complicated:

 

  1. Let's suppose there are N sections, and text section is number i. We are going to append our code, so you have to physically insert it. You have to make modifications regarding program headers and section headers, in order to make the new code "fit":
    1. All the references to file offsets greater that the insertion point would have to be increased (p_offset, sh_offset if we talk elf language).
    2. The .text section and code segment must be larger. As much as the size of the new code (sh_size, p_filesz).
    3. The new code occupies virtual addresses that were previously assigned to other stuff, so the starting virtual and physical address of sections i+1 to N (and the suitable segments at program headers) must be appropriately increased (p_vaddr, p_paddr, sh_addr).

     

  2. That was the easy part, i did it (don't include it because it's not a very educational material, but you may ask for it if you want), but it's not enough ... it does not work. Why ? let's see a usual distribution of sections in an easy program:
Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .interp       00000013  080480d4  080480d4  000000d4  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .hash         00000098  080480e8  080480e8  000000e8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .dynsym       00000130  08048180  08048180  00000180  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .dynstr       000000c7  080482b0  080482b0  000002b0  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .rel.got      00000008  08048378  08048378  00000378  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .rel.bss      00000008  08048380  08048380  00000380  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .rel.plt      00000030  08048388  08048388  00000388  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .init         0000002c  080483c0  080483c0  000003c0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  8 .plt          00000070  080483ec  080483ec  000003ec  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  9 .text         000000c8  0804845c  0804845c  0000045c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 10 .fini         0000001c  08048530  08048530  00000530  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .rodata       00000010  0804854c  0804854c  0000054c  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 12 .data         00000004  0804955c  0804955c  0000055c  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 13 .ctors        00000008  08049560  08049560  00000560  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 14 .dtors        00000008  08049568  08049568  00000568  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 15 .got          00000028  08049570  08049570  00000570  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 16 .dynamic      00000098  08049598  08049598  00000598  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 17 .bss          00000004  08049630  08049630  00000630  2**2
                  ALLOC
 18 .comment      00000064  00000000  00000000  00000630  2**0
                  CONTENTS, READONLY
 19 .note         00000064  00000064  00000064  00000694  2**0
                  CONTENTS, READONLY

If you look at some function present in the symbol table that is dynamically loaded (printf for instance), you'll get an address:

080483fc DF *UND* 00000024
printf 

so 80483fc is the supposed address for printf in our program. There you'll not find printf's code, but a jmp [index_table]. The contents of the table are initialized by dynamic loader. We see, we're lucky because that address (80483fc) is from .plt section, and that address is not modified by the insertion of the new code. But if we go on, we see:

080483fc jmp *0x804957c 

so the data table that keeps the real address of dynamic function, located in section .got, is disturbed by our patch. If we add, say 10k of code, *0x804957c is not pointing .got section anymore. It is pointing some innocent instruction inside .text section.

You would need to patch .plt section, that could be big, and well, i gave up here. The problem is not unsolvable, may be patient programmer could do :-). Anyway you'll agree the procedure is getting more confused and less elegant.

 

2. TRACING A PROCESS

The idea of this approach is taken from ltrace written by Juan Céspedes. What he does is read symbol table and place breakpoints before calling dynamically linked functions. Read the stack, call the function and keep the returned value. That job is accomplished by ptrace, a system call that let you "debug" a process.

What i want to do here is to patch code and data on the fly in order to suit my needs: change the calling parameter, create calling loops to validating functions, ... The program will know nothing :). Ptrace provides elements to modify code/data, read code/data, insert breakpoints, resume execution, trap signals, trap system calls, etc ... almost all you want. I'd like to remark that, WITH PTRACE YOU HAVE GOT ALL THE POWER.

The main lacks for this approach are:

  1. It's slow. If you're trying a brute force approach you need all the speed from you computer, and ptrace calls are not fast. Don't misunderstand, may be just microseconds, but in some scenario it could be important.
  2. All my access to victim's address space is done via ptrace. I cannot modify data/code directly, moreover i cannot jump directly to foreign code nor return from it. Of course, i can give my own functions to be called on trap signals and so one, but it's like a firewall that separates both processes.

It would be nice to manipulate the kernel and make the trick, but that's far beyond my available time ... and you'll see there are other alternatives. To get this approach closer to you let's see a simple piece of code dealing with ptrace. It creates a child process that will be debugged by its parent, then the child process executes the victim program. I will not explain what is a child/parent process, or how a fork call works, but there are thousands of books that will inform you. On the other hand i would like to tell you about a good guide for ptrace's call, but sadly i did not find it, so consult man page.

#include 
#include 
#include 
#include 
#include 
#include 
#include 
void main(int ARGC, char *ARGV[]){
 int  pid, status;

 pid=fork();
 if (pid==-1) perror("fork");
 else if (pid==0){
  /* this code will be executed ONLY by the child process */
 
   signal(SIGTRAP, SIG_IGN); 
   if ( ptrace(PTRACE_TRACEME, 0, 0, 0) == -1)
     perror("ptrace");
   printf("\nProcess controlled\n");
   if (execlp("./the_victim_program",
"./the_victim_program", NULL)== -1) 
     perror("execlp");
 }
 else{ 
 
 /* this code will be executed ONLY by the parent process  
    */
 /* it will wait until receives a signal from the child process */
 
     wait(&status);
     if ( WIFSTOPPED(status) ) printf ("\nSignal: %i\n",
WSTOPSIG(status) );
     
 /* the debugged process is stopped (with sigtrap) and the
parent */
 /* receives the signal. The process is stopped before starting. Here */
 /* you can change whatever you want. With "sigaction" you
can handle */
 /* signals (for instance sigtrap) with the function you fancy        */
 
 /* After changing whatever the execution is continued */
 
     if (ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status))==-1)
perror("ptrace");
     wait(&status);
     
 /* wait until child process exits */
 
 }
}

 

3. MANIPULATING DYNAMIC LINKER

What we are trying to do is to write our own code and make it visible from our victim's code. We are trying to share our code, if you want to put that way. But that is done everyday with no artificial method: dynamic linker. So we are going to try some trick or otherwise try-to-emulate what the linker does.

The most obvious and easy trick, but not less powerful is the well known LD_PRELOAD trick. You can find it documented (though the procedure has to be tuned :-) together with dynamic liker (ld-so) source code. It was mentioned in zlib package and other docs too. The trick was also explained by adq in a previous essay. Anyway, as far as i've read it is always used to fake some function dynamically loaded, but never to access victim's code from the faked function.

Example:

Suppose our evil program uses some common function as sin, you could preload it (via LD_PRELOAD env. variable) and load in memory your "holy" code. You could always resolve the problem of the actual function along with cos function, for example ;). Now pass (or hardwire) the address of a validating function to our preloaded library (or to our preloaded sin function if you feel like that), and patch the executable (physically or ptracing it) to call the faked sin function instead of the validating function. Then from your faked function you "see" the calling address space and you can call the validating function a zillion times until you get a good value, and you can then return to the caller code :-). You get ????

Before

LOCAL CODE
call validating_function 
   :validating_function
      ....
    ret
check_it

 

After

LOCAL CODE                              
LIBRARY CODE
call sin       -------------------->    :my_preloaded_faked_sin
                                         ...
                                           call validating function
                                           ret
check_it      

I like the above method especially because it's fairly easy and is the dynamic loader the one who makes the dirty job of loading pages into memory. Moreover the patching is minimum, but it is good to know other alternatives, that will do more or less what the dynamic linker does in background. Keep in mind this easy method can only be used in dynamic executables, that although they are the most usual, sometimes you find statically linked ones.

Firstly you can try to load a library in a way similar to the way you do in Windoze. The most important drawback is that you need not only to overwrite code from the executable, but you need that the functions involved were previously used by the program (as far that they have to be present in the dynamic table). The functions involved are:

void *dlopen (const char *filename, int flag)

void *dlsym (void *handle, char *symbol)

int dlclose (void *handle)

so you will load by yourself (from the victim's code) a library and get the address of a particular function or symbol. The you can jump to it.

Another alternative, more sophisticated but easier to implement is to introduce the system call mmap. It is a very interesting service that maps a file into memory with the corresponding attributes. If you wondered is the way dynamic loader "loads" libraries into memory:

void * mmap (void *start, size_t length, int prot , int flags, int fd, off_t offset);

mmap, remember is a system call, no library is necessary. It's a great gift from kernel guys. The system call returns a pointer to the loaded area. If you use in the "flag" parameter the value MAP_FIXED, the parameter start should indicate the address where YOU WANT the code/data to be loaded. I didn't try yet, but what about if you overwrite the code previously loaded ... it would not work as it is read-only data..

 

Final Notes

Well, that's all. Lots of material to play with. I have not given ready-to-make-recipes because if you have the interest you will build by yourself the procedures you need with the clues given here. But you can always ask whatever you feel is not clear enough. Of course, if you've just installed linux for the first time, this will not be useful for you and you better read previous essays in order learn something

Ob Duh

I WILL bother explaining you that you should TEST by yourself what is explained here and try to find new ways to make accesible your code to a non-colaborating process.


You are deep inside fravia's page of reverse engineering, choose your way out:

red

redhomepage redlinks red+ORC redbots wars redstudents' essays redcounter measures
redbots wars redantismut CGI tricks redacademy database redtools redjavascript tricks
redcocktails redsearch_forms redmail_fravia+
redIs software reverse engineering illegal?