Skip to content
/ zpoline Public
forked from yasukata/zpoline

An efficient and versatile system call hook mechanism

License

Notifications You must be signed in to change notification settings

lck0/zpoline

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zpoline: hooking system calls without pain

Zpoline is a novel system call hook mechanism that offers the following advantages.

  • 100 times faster than ptrace.
  • 100% coverage, namely, it can exhaustively hook system calls.
  • No need for the source code of user-space programs.
  • No need for the change to the OS kernel and no kernel module is necessary.

Therefore, Zpoline is a quite good option if you think...

  • ptrace is too slow for your project.
  • the LD_PRELOAD trick is not enough because it cannot exhaustively hook system calls.
  • you cannot anticipate the availability of the source code of your hook target.
  • you do not want to modify the OS kernel or install a kernel module.

Zpoline is categorized into binary rewriting, but you do not need to worry that your program binary files are overwritten. The setup procedure of Zpoline rewrites the code binary loaded on the memory, just before the user-space program starts its main function. Therefore, it does not overwrite your program binary files.

The cool part of Zpoline is that it does not fail to hook system calls, which is difficult for existing binary rewriting techniques.

The key ideas of Zpoline are to exploit the calling convention and instantiate a special trampoline code. The overview is shown in the picture below.

In a nutshell, Zpoline replaces the syscall and sysenter instructions with callq *%rax, and crafts a trampoline code at virtual address 0 (Zero). That is why this technique is named Zpoline.

For more technical details, please check Documentation/README.md.

You can find a simpler version at commit e5afaba which does not use dlmopen.

Target Platform

Currently, this implementation assumes Linux on the x86-64 architecture.

Dependency

Zpoline uses the disassembler in libopcodes that is part of binutils.

$ sudo apt install binutils-dev

Files

This example uses two shared libraries.

  1. apps/basic/libzphook_basic.so only implements the hook function. (we call the hook function library).
  2. libzpoline.so is loaded by LD_PRELOAD. This configures the trampoline code, conducts binary rewriting, and loads ./apps/basic/libzphook_basic.so using dlmopen.

Build

To build apps/basic/libzphook_basic.so, please type the following command.

$ make -C apps/basic

For libzpoline.so, please type the following command.

$ make

Setup

To use Zpoline, please set 0 to /proc/sys/vm/mmap_min_addr.

$ sudo sh -c "echo 0 > /proc/sys/vm/mmap_min_addr"

How to Use

Pleae specify apps/basic/libzphook_basic.so for the LIBZPHOOK environment variable, and libzpoline.so for LD_PRELOAD. The example command is as follows.

$ LIBZPHOOK=./apps/basic/libzphook_basic.so LD_PRELOAD=./libzpoline.so [program you wish to run]

LIBZPHOOK is defined in main.c of libzpoline.so. libzpoline.so performs dlmopen for a shared library file specified by LIBZPHOOK.

Currently, the hook function in apps/basic/libzphook_basic.so prints the system call number using printf. The following is the example output.

$ LIBZPHOOK=./apps/basic/libzphook_basic.so LD_PRELOAD=./libzpoline.so /bin/ls
Initializing Zpoline ...
-- Setting up trampoline code
-- Rewriting the code
Loading hook library ...
-- load ./apps/basic/libzphook_basic.so
-- call hook init
output from __hook_init: we can do some init work here
-- set hook function
output from __hook_fn: syscall number 1
Start main program
output from __hook_fn: syscall number 257
output from __hook_fn: syscall number 5
output from __hook_fn: syscall number 9
output from __hook_fn: syscall number 3
output from __hook_fn: syscall number 16
output from __hook_fn: syscall number 16
output from __hook_fn: syscall number 257
output from __hook_fn: syscall number 5
output from __hook_fn: syscall number 217
output from __hook_fn: syscall number 217
output from __hook_fn: syscall number 3
output from __hook_fn: syscall number 1
apps  Documentation  libzpoline.so  LICENSE  main.c  main.o  Makefile  README.md
output from __hook_fn: syscall number 3

Why do we need to separate files and load by dlmopen?

Users of the Zpoline technique should pay attention to the use of rewritten functions, otherwise, the system call hook may fall into an infinite loop.

Let's think about the printf library call which internally invokes a write system call. When Zpoline is applied to the user-space program, the write system call triggered in printf will be hooked by Zpoline. The problem occurs when the system call hook calls printf. It will result in an infinite loop because the write system call in printf, called by the hook function, will be hooked and redirected to the same hook function.

dlmopen releases users of Zpoline from this issue.

The implementation of the hook function is __hook_fn in apps/basic/main.c.

As mentioned above, the hook function may fall into an infinite loop when it uses a library call such as printf. But, in this example, the hook function (__hook_fn) can use printf.

This is realized by dlmopen. In libzpoline.so, a function named load_hook_lib calls dlmopen and loads apps/basic/libzphook_basic.so. In particular, load_hook_lib specifies LM_ID_NEWLM for dlmopen, and this requests to load apps/basic/libzphook_basic.so in a new namespace. At the same time, dlmopen also loads other required libraries including libc in the same namespace to which apps/basic/libzphook_basic.so belongs.

Now, libc for __hook_fn is newly instantiated in the new namespace, and it is different from the one used by the primary user-space program. Here, libzpoline.so does not replace syscall and sysenter instructions in the newly instantiated libc. Therefore, __hook_fn does not cause an infinite loop.

After apps/basic/libzphook_basic.so is loaded, libzpoline.so accesses __hook_fn in apps/basic/libzphook_basic.so through a pointer named hook_fn in this example.

Note that dlmopen does not only load libc but also other shared libraries associated with the hook function library. The association is done by the compiler. If you forget to specify the library to link (e.g., -lpthread for libpthread, -lm for libmath) for the compiler, dlmopen may fail to load them.

How to Implement My Own System Call Hook

Currently, libzpoline.so is independent of the hook function library. So, you can build your own hook function library, and to activate it, you only need to specify it to the LIBZPHOOK environment variable.

In the hook function library, you should implement these two.

  1. __hook_fn is the system call hook function.
  2. __hook_init is called just after trampoline code instantiation and binary rewriting are completed. We can use this for some specific initialization tasks.

Please keep these function names so that dlsym in main.c can find your own implementations. Or, please change the argument for dlsym in load_hook_lib accordingly.

Further Information

You may be able to have a better understanding by checking the comments in the source code and Documentation/README.md.

About

An efficient and versatile system call hook mechanism

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C 93.5%
  • Makefile 6.5%