|
|
The basic features of ptrace were explained in Part I. In Part II we saw a small program which accessed the registers of a process and modified them so as to change the output of that process, by injecting some extra code. This time we are going to access the memory of a process. The purpose of this article is to introduce a methods for infecting binaries on runtime. There are many possible areas of use for this technique. 1. Introduction.We are familiar with ptrace and know the techniques of attaching a process, how to trace it and finally to free it. We also have an idea about the structure of the Linux binary format - ELF.
Our plan is to fetch/modify a running binary. So we have to locate the
symbols inside the binary. There we need
The foramt of link_map is (from /usr/include/link.h)
A small explanation for the fields.
Link-map is a linked list, each item on list having a pointer to loaded library. What we have to do is, to follow this chain, go through every library and find our symbol. Now we have a question. Where we can find this link_map?
For every object file, there is a global offset table (GOT) which contains many details of the binary. In GOT, the second entry is dedicated for the link_map. So we get the address of link_map from GOT[1] and we go on searching our symbol.
2. Straight to code.Now we have collected the basic information needed to access the memory. Let's
start now. First of all we attach the process 'pid' for tracing. Now we go for
finding out the link_map we require. You will find functions
The function for locating the link_map is:
We start from the location 0x08048000 to get elf header of the process we are tracing. We get the elf header and from its fields we can get the program header. (The fields of headers were discussed in Part II.) Once we get the program header, we go on checking for the header with dynamic linking information. From the header/struct with dynamic linking information, we fetch the location of the information. Go on searching until we get the base address of global offset table.
Now we have the address of GOT with us and take the second entry of GOT (there we have link_map). From there get the address of the link_map which we require and return.
We have the struct link_map and we have to get symtab and strtab. For this,
we move to
The function resolv_tables is:
What we actually do here is just reading dynamic sections one by one and checks
whether the tag is DT_STRTAB or DT_SYMTAB. If yes, we can get their respective
pointers and assign to
Our next step is getting the value of symbol from the symbol table. For this we take every symbol table entry one by one and check it whether it's a function name. (We are interested in finding the value of a library function). If it is then it's compared with the function name given by us. If here also they match now the value of the symbol is returned.
Now we have got the value of the symbol what we actually required. What help will the value do for us? The answer depends upon the reader. As I have already stated we may use this for both good and evil purposes.
You might be thinking that everything is over. We forgot a step that we shouldn't forget - detaching the traced process. This may leave the process in a stopped state for ever and the consequences are already discussed in Part I. So our last and final step is to detach the traced process.
The program may be obtained from. Ptrace.c Almost the whole code is self explaining.
Compile it by typing
Now we want to test the program. Run some process in some other console, come
back and type.
(Here my test program is
and watch what is going on.
3. Conclusion.So, we come to the end of a series of three articles which has gone through
the basic programming with
All Suggestions, Criticisms, Contributions etc. are welcome. You can contact me at busybox@sancharnet.in Published in Issue 85 of Linux Gazette, December 2002 |