Linux kernel-level backdoor principles and simple practices

Source: Internet
Author: User

User space and kernel space
---------------------------

Linux is a protected operating system. It has been working in i386 CPU protection mode.

The memory is divided into two units: the kernel area and the user area. (Translator's note: I think this is more convenient) the kernel area stores and runs the core code. Of course, as the name suggests, the user area also stores and runs the user program. Of course, as a user process, it cannot access the memory space in the kernel region and the address space of other user processes.

Unfortunately, the same is true for core processes. The core code cannot access the address space in the user zone.
So what is the significance of this? Well, let's assume that when a hardware driver tries to write data to a user's memory space program, it cannot be done directly, however, it can be accomplished indirectly using some special core functions. Similarly, when the parameter needs to be passed to the core function, the core function cannot directly read the parameter. Similarly, it can use some special core functions to pass parameters.

Here are some useful core functions used to pass parameters between the kernel and the user zone.

# Include <ASM/segment. h>

Get_user (PTR)
Obtains the given bytes, characters, or long integer from the user memory. This is just a macro (detailed definition of this macro is included in the core code), and it determines the Transfer Quantity Based on the parameter type. So you must use it skillfully.

Put_user (PTR) and get_user () are very similar. However, instead of reading data from the user memory, put_user (PTR) wants the user memory to write data.

Memcpy_fromfs (void * To, const void * From, unsigned long N)
Copy n Bytes from * from the user memory to the pointer to the core memory *.

Memcpy_tofs (void * To, const * From, unsigned long N)
Copy n bytes of data from * from core memory to * To in user memory.

/* Note: these four functions are sufficient to solve the parameter passing problem in the kernel and user zone in 2.0.x.
Version has a new implementation, that is, copy_user_to (...) and copy_user_from (...) according to the kernel version
Special functions may vary. Please pay attention to the implementation methods of the core code. */

System Call

Most C-function library calls depend on system calls, which are simple core packaging functions that can be called by user programs. These system calls run in the kernel itself or in the kernel module that can be loaded and detached dynamically.

Like many other systems, system calls in Linux depend on a given interrupt to call multiple system calls. In Linux, the interrupt is int 0x80. When the call to 'int 0x80' is interrupted, the control is transferred to the kernel (or, to be exact, to the _ system_call () function ), in addition, it is actually a single processing process in progress.

* _ System_call () How does it work?

First, all registers are saved and % eax registers fully check the system call table, which lists all system calls and their address information. It can be accessed through extern void * sys_call_table. Each defined value and memory address in the table correspond to each system call. You can find the number of system calls in the/usr/include/sys/syscall. h header. They correspond to the corresponding sys_systemcall name. If a system call does not exist, the corresponding identifier in sys_call_table is 0 and an error message is returned. Otherwise, the system call exists and the corresponding entry in the table is the memory address of the System Call code.

Here is a problematic system call routine:

[Root @ plaguez kernel] # Cat no1.c
# Include <Linux/errno. h>
# Include <sys/syscall. h>
# Include <errno. h>

Extern void * sys_call_table [];

SC ()
{// 165 the system call number does not exist.
_ ASM __(
"Movl $165, % eax
Int $0x80 ");
}

Main ()
{
Errno =-SC ();
Perror ("Test of invalid syscall ");
}
[Root @ plaguez kernel] # GCC no1.c
[Root @ plaguez kernel] #./A. Out
Test of invalid syscall: function not implemented
[Root @ plaguez kernel] # exit

System Control is switched to real system calls to complete your request and return it. Then _ system_call () calls _ ret_from_sys_call () to check different return values, and finally returns them to the user memory.

* Libc

This int $0x80 is not directly used for system calling. More specifically, libc functions are often used to wrap 0x80 interruptions.

Libc generally uses the _ syscallx () macro to describe system calls. X is the total number of system calls.

For example, write (2) in libc is implemented using the _ syscall3 system calling macro, because the actual write (2) prototype requires three parameters. Before calling the 0x80 interrupt, The _ syscallx macro assumes the stack structure of the system call and the list of required parameters. Finally, when _ system_call () (triggered by Int & 0x80) is returned, the _ syscallx () Macro will detect the error return value (in % eax) and set errno for it.

Let's take a look at another write (2) routine and see how it is preprocessed.

[Root @ plaguez kernel] # Cat no2.c
# Include <Linux/types. h>
# Include <Linux/fs. h>
# Include <sys/syscall. h>
# Include <ASM/unistd. h>
# Include <sys/types. h>
# Include <stdio. h>
# Include <errno. h>
# Include <fcntl. h>
# Include <ctype. h>

_ Syscall3 (ssize_t, write, Int, FD, const void *, Buf, size_t, count);/* construct a write call */

Main ()
{
Char * t = "this is a test. N ";
Write (0, T, strlen (t ));
}
[Root @ plaguez kernel] # gcc-e no2.c> no2.c
[Root @ plaguez kernel] # indent no2.c-KR
Indent: no2.c: 3304: Warning: Old Style assignment ambiguity in "=-". Assuming "= -"

[Root @ plaguez kernel] # tail-N 50 no2.c

#9 "no2.c" 2

Ssize_t write (int fd, const void * Buf, size_t count)
{
Long _ res;
_ ASM _ volatile ("int $0x80": "= A" (_ res): "0" (4 ), "B" (long) (FD), "C" (long) (BUF), "D" (long) (count )));
If (_ res> = 0)
Return (ssize_t) _ res;
Errno =-_ res;
Return-1;
};

Main ()
{
Char * t = "this is a test. N ";
Write (0, T, strlen (t ));
}
[Root @ plaguez kernel] # exit

Note that the "0" parameter in the write () matches sys_write, which is defined in/usr/include/sys/syscall. h.

* Build your own system call.

Here are several methods to build your own system call. For example, you can modify the kernel code and add your own code. A relatively simple and feasible method, however, must be written as capable of loading the kernel module.

No code can be loaded at any time as the kernel module can.

Our main intention is to need a very small kernel. When we need the insmod command, the given driver can be automatically loaded. In this way, the lkm program to be detached is much easier than writing code in the kernel code tree.

* Write lkm programs

An lkm program can be easily written in C. It contains a large number of # defines, some functions, a function of the initialization module, called init_module (), and an unmount function: cleanup_module ().

Here is a classic lkm code structure:

# Define Module
# DEFINE _ KERNEL __
# DEFINE _ kerne_syscils __

# Include <Linux/config. h>
# Ifdef Module
# Include <Linux/module. h>
# Include <Linux/version. h>
# Else
# Define mod_inc_use_count
# Define mod_dec_use_count
# Endif

# Include <Linux/types. h>
# Include <Linux/fs. h>
# Include <Linux/mm. h>
# Include <Linux/errno. h>
# Include <ASM/segment. h>
# Include <sys/syscall. h>
# Include <Linux/dirent. h>
# Include <ASM/unistd. h>
# Include <sys/types. h>
# Include <stdio. h>
# Include <errno. h>
# Include <fcntl. h>
# Include <ctype. h>

Int errno;

Char TMP [64];

/* Suppose we want to use IOCTL to call */
_ Syscall3 (INT, ioctl, Int, D, Int, request, unsigned long, ARG );

Int myfunction (INT parm1, char * parm2)
{
Int I, J, K;
/*...*/
}

Int init_module (void)
{
/*...*/
Printk ("nmodule loaded. N ");
Return 0;
}

Void cleanup_module (void)
{
/*...*/
}

Check the # defines (# define module, # DEFINE _ KERNEL _) and
# Des (# include <Linux/config. h> ...)

It must be noted that our lkm is running in the kernel state, so we cannot use libc-encapsulated functions, but we can use the _ syscallx () discussed earlier () macro to build system calls.

You can compile your module 'gcc-C-O3 module. c' and load it using 'insmod module. o.

We recommend that you use lkm to modify the kernel code without completely recreating the core code. For example, you can modify the Write System Call to hide some of the given files, just as we put our backdoors in a very good place: what happens when you no longer trust your system kernel?

* Backdoor for kernel and system call

After briefly introducing the above theory, what can we do. We can facilitate lkm to intercept some system calls that affect us, so that we can force the kernel to run in our way. For example, we can use the ioctl system call to hide the display of the NIC promisc mode caused by sniffer. Very effective.

To change a given system call, you only need to add a definition extern void * sys_call_table [] to your lkm program, and use init_module () function to change the entry in sys_call_table to point to our own code. The changed call can do everything we want it to do, use the changed sys_call_table to export more original system calls, and ....

Translator's post: This article is relatively easy to understand, so it can be used as an introduction to lkm programming. It focuses on the principle of Linux system calling system call, and how we can intercept it through lkm and change it to the code we want to build a backdoor program. Again, this article is based on the Linux kernel version 2.0.x. When implementing your own system, please compare the kernel code to make changes.

Previous Article: [Linux programming] Linux/Unix Process Creation related documents: Linux kernel space protection
Next article: how to compile a Linux Device Driver

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.