12

I am writing a tool. A part of that tool will be its ability to log the parameters of the system calls. Alright I can use ptrace for that purpose, but ptrace is pretty slow. A faster method that came to my mind was to modify the glibc. But this is getting difficult, as gcc magically inserts its own built in functions as system call wrappers than using the code defined in glibc. Using -fno-builtin is also not helping there.

So I came up with this idea of writing a shared library, which includes every system call wrapper, such as mmap and then perform the logging before calling the actual system call wrapper function. For example pseudo code of what my mmap would look like is given below.

int mmap(...)
{
 log_parameters(...);
 call_original_mmap(...);
 ...
}

Then I can use LD_PRELOAD to load this library firstup. Do you think this idea will work, or am I missing something?

MetallicPriest
  • 29,191
  • 52
  • 200
  • 356
  • 8
    It probably won't work for statically linked executables. And it won't work for executables doing syscalls without going thru libc. – Basile Starynkevitch May 21 '12 at 17:25
  • Do you want to intercept every time an actual sys call is performed (at each `int 0x80`), or at each call to a library handler functions? – Chris May 22 '12 at 02:39
  • I think it will probably be slow. That is basically exactly what valgrind does for its instrumentation, and despite being a very well known and developed tool, it still kills performance. I think if there was a faster way, it would have been used already there. My guess is that you underestimate how much the logging function will cost, and there's no way around that. – zebediah49 Jun 07 '12 at 18:35
  • take a look at `fakeroot`, it uses a shared library with a bunch of libc function wrappers, which is loaded with `LD_PRELOAD`. –  Jun 26 '12 at 14:16
  • @jthill, my sentiments exactly. I use `strace` very often for this exact purpose. I *just* checked which syscalls libc6-2.15 ends up calling, and with which parameters, when using `opendir()` and `fstatat()`. – Nominal Animal Jul 04 '12 at 21:09
  • Have a look at `ltrace`. It sounds like you are re-implementing it. – snap Jul 05 '12 at 10:14

4 Answers4

3

No method that you can possibly dream up in user-space will work seamlessly with any application. Fortunately for you, there is already support for doing exactly what you want to do in the kernel. Kprobes and Kretprobes allow you to examine the state of the machine just preceeding and following a system call.

Documentation here: https://www.kernel.org/doc/Documentation/kprobes.txt

Max DeLiso
  • 1,213
  • 11
  • 23
1

As others have mentioned, if the binary is statically linked, the dynamic linker will skip over any attempts to intercept functions using libdl. Instead, you should consider launching the process yourself and detouring the entry point to the function you wish to intercept.

This means launching the process yourself, intercepting it's execution, and rewriting it's memory to place a jump instruction at the beginning of a function's definition in memory to a new function that you control.

If you want to intercept the actual system calls and can't use ptrace, you will either have to find the execution site for each system call and rewrite it, or you may need to overwrite the system call table in memory and filtering out everything except the process you want to control.

jmkeyes
  • 3,751
  • 17
  • 20
0

All system calls from user-space goes through a interrupt handler to switch to kernel mode, if you find this handler you probably can add something there.

EDIT I found this http://cateee.net/lkddb/web-lkddb/AUDITSYSCALL.html. Linux kernels: 2.6.6–2.6.39, 3.0–3.4 have support for system call auditing. This is a kernel module that has to be enabled. Maybe you can look at the source for this module if it's not to confusing.

Joelmob
  • 1,076
  • 2
  • 10
  • 22
  • 2
    LSM can be used to control syscalls too. http://en.wikipedia.org/wiki/Linux_Security_Modules – osgx May 21 '12 at 23:10
  • This is not universally true - it might be true for x86 on certain versions of certain OSes, but there are many platforms and/or OSes that do not use an interrupt as the user-mode to kernel transition mechanism. – twalberg Jul 19 '12 at 18:22
0

If the code you are developing is process-related, sometimes you can develop alternative implementations without breaking the existing code. This is helpful if you are rewriting an important system call and would like a fully functional system with which to debug it.

For your case, you are rewriting the mmap() algorithm to take advantage of an exciting new feature(or enhancing with new feature). Unless you get everything right on the first try, it would not be easy to debug the system: A nonfunctioning mmap() system call is certain to result in a nonfunctioning system. As always, there is hope.

Often, it is safe to keep the remaining algorithm in place and construct your replacement on the side. You can achieve this by using the user id (UID) as a conditional with which to decide which algorithm to use:

if (current->uid != 7777) {
/* old algorithm .. */
} else {
/* new algorithm .. */
}

All users except UID 7777 will use the old algorithm. You can create a special user, with UID 7777, for testing the new algorithm. This makes it much easier to test critical process-related code.

kannah
  • 51
  • 6