MODEST: dynamic binaries defeated and some improvements added!!

The matter

As we know reading the first white-paper concerning MODEST project, our LKM was not able to capture the system calls requested by a dynamic linked binary. This awful issue was because of the fact that, in modern GNU/Linux Kernels, any System Call will be served through a call to the __kernel_vsyscall(); function, and, in the concrete case of dynamic binaries, this call will be translated in a sysenter/sysexit Ring0 mechanism. So, we could not catch any system call using our modest_syshandler() routine, which was useful only in the classic old-days Ring0 mechanism well-known as int 0x80.

The solution

Instead of writting some code to alter the MSR_* registers so as to point at a new kernel entry point for the sysenter call, I decided to use Kprobes, as well-described in my last white-paper. This way, I skipped the awful and difficult way of writting assembly code, and all I needed was to read the Kprobes documentation carefully and write a few C-high code lines to accomplish this goal. This piece of work is extensively explained and described in the last white-paper written expressly for this new MODEST behavior.

In short …

First of all, I decided I was in need of writting a pre-handler. This pre-handler would be executed as soon as the sysenter call was requested by a process. Therefore, my pre-handler routine would get all the interesting parameters, stored in the pt_regs data structure and accessible directly using C code. Finally, it would call my own my_sys_write() or my_sys_writev() functions so as to write the data to the fd’ descriptor, as usual.

The main problem to do that was related to where I had to put this handler. I mean, the exact memory address instruction. Logically, the first option could be at the sysenter entry point itself. But that was not possible ’cause my pre-handler routine had to use the current macro, and this macro had to get the task_struct * data structure for the current process from the stack. So, the pre-handler had to be placed right after the assembly call to movl TSS_sysenter_esp0(%esp),%esp. Looking at the file where GNU/Linux sysenter/sysexit are implemented, I discovered where this address would come to be:

267     # sysenter call handler stub
268 ENTRY(sysenter_entry)
269     CFI_STARTPROC simple
270     CFI_DEF_CFA esp, 0
271     CFI_REGISTER esp, ebp
272     movl TSS_sysenter_esp0(%esp),%esp
273 sysenter_past_esp:
274     /*
275      * No need to follow this irqs on/off section: the syscall
276      * disabled irqs and here we enable it straight after entry:
277      */
278     sti

Thus, I added the pre-handler at sysenter_past_esp address, using Kprobes API – it was really a piece of cake, that’s for sure 😉 -, and calling the Kernel aid kallsyms_lookup_name(), as described in the paper.

Below, the pre-handler in charge of catching any call to sys_write() or sys_writev() system calls requested by a binary linked in a dynamic way:

 95 int kprobe_prehandler (struct kprobe *p , struct pt_regs *regs){
 96     if(pid_affected!=-1 && (regs->eax==__NR_write||regs->eax==__NR_writev )){
 97         /* Get the parameters through the registers : */
 98         bytes_to_read = regs->edx;  // size_t count
 99         userfd = regs->ebx;         // int fd
100         memory = (const char __user *)regs->ecx;
102         if(pid_affected==current->pid && userfd==oldfd){
103             if(atomic_read(&syscall_first_catched)==0){
104                 printk(KERN_INFO "MODEST %s by %s: PID %d on cpu %d ==> [sysenter/sysexit]\n",
105                     _VERSION_, _AUTHOR_, current->pid , current->thread_info->cpu);
106                 atomic_inc(&syscall_first_catched);
107             }
108             switch(regs->eax){
109                 case __NR_write:
110                         schedule();
111                         my_sys_write(krn_fd, memory, bytes_to_read );
112                     break;
113                 case __NR_writev:
114                         schedule();
115                         my_sys_writev(krn_fd,(const struct iovec __user*)memory,
116                             bytes_to_read);
117                     break;
118             }
119         }
120     } return 0; /* Return to sys_write or sys_writev or whichever ! ;- */
121 }

There’s a demonstrative video showing how MODEST can be used, finally, to peep data written by a given process, no matter if it is static or dynamic HERE.

Pausing and resuming the “peeping” process

The first improvement added to the MODEST project concerns a new “option” available at any time using the user-space utility umodest. Now, as long as a certain static or dynamic binary is being intercepted with the “-d” flag, the user can press the “p” key to resume this catching process, and whenever he or she desires, press the “r” key so that the peeping process can continue in a normal way. There’s a demonstrative video HERE where this option is fully auto-explained.

Below, the “new” umodest tool’s appearance:

Running shared tests (-d) over /tmp/modest-tests…
Running with PID: 2746
Redirecting fd: 3 to: /tmp/modest-tests.
When you want to restore original fd, just type q ENTER
Push p ENTER to pause the Ring 0 catching system.
Push r ENTER to resume the Ring 0 catching system.

More verbosity

Continuing with our last additions to the MODEST source code, there’s now a more detailed level of verbosity, as far as the GNULinux Kernel is concerned. Any ioctl() call sent by the umodest utility to the driver, such as pausing, resuming or restoring the process FDT, a printk() message will show up, readable using dmesg, syslog or whatever.

In addition, another printk() message will be printed out as soon as the first system call will be executed, showing this way what sort of Ring 0 mechanism  is being used by the current process. All this messages are shown below:

Load successful. Compiled at Aug 26 2009 17:59:21
@ new syscall  (int $0x80) at: 0xd09f9970
@ sysenter_past_esp at: 0xc0102bbb , catched by 0xd09f97fa
Restore FDT requested ...
PID 2548 on cpu 0 ==> [sysenter/sysexit]
Pause peeping has been requested  ...
Resuming peeping process  ...
Pause peeping has been requested  ...
Resuming peeping process  ...
Restore FDT requested ...

More information

Take a look at the white-paper concerning Kprobes and dynamic binaries HERE – written in LaTeX.

Here you have a lot of interesting demonstrative videos showing different aspects of using MODEST via Kprobes and dynamic linked binaries.

Of course, get the latest MODEST sources using the CVS at SourceForget net throught the kmodest web page :