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.

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 link_map. link_map is
the dynamic
linker's internal structure with which it keeps track of loaded libraries
and symbols within libraries.

l_addr: Base address where shared object is loaded.
This value can also be found from /proc/<pid>/maps

l_name: pointer to library name in string table

l_ld : pointer to dynamic (DT_*) sections of shared lib

l_next: pointer to next link_map node

l_prev: pointer to previous link_map node

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.

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 read_data,
read_str etc. These are helper functions to make working with ptrace easier.
Helper functions are self explaining.

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 l_ld field of link_map and traverse through dynamic sections until
DT_SYMTAB and DT_STRTAB have been found, and finally we can seek our symbol
from DT_SYMTAB. DT_SYMTAB and DT_STRTAB are the addresses of symbol table and
string table respectively.

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 strtab and symtab. Once the dynamic sectoins are
over, we can stop.

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

#cc Ptrace.c -o symtrace

Now we want to test the program. Run some process in some other console, come
back and type.
(Here my test program is emacs and the symbol I give is strcpy).
You may trace any program that is traceable instead of emacs and any symbol
you want to inspect.

So, we come to the end of a series of three articles which has gone through
the basic programming with ptrace. Once you have understood the basic
concept it is not difficult to make steps by your own. More details on ptrace
and elf are available at
www.phrack.org. One more thing
I have to write is that, we reached here without even mentioning a major topic.
One major feature of ptrace is its play with system calls. In User Mode Linux,
this feature is used in a large scale. I am busy with my classes and final year
project, and I promise, if time permits we will continue this series and then
we will have a look at those features of ptrace.