首页 > 代码库 > 反病毒攻防研究第015篇:病毒感染标志的添加

反病毒攻防研究第015篇:病毒感染标志的添加

一、前言

        对于感染型病毒而言,如果对同一个目标文件多次进行感染,有可能导致目标文件损坏,使得无法执行。所以病毒程序往往会在第一次感染时对目标文件写进一个感染标志,这样在第二次遇到该文件时,首先判断一下该文件中是否包含有感染标志,如果有,则不再感染,如果没有感染标志则进行感染(关于文件的感染,可参见《反病毒攻防研究第004篇:利用缝隙实现代码的植入》和《反病毒攻防研究第005篇:添加节区实现代码的植入》)。所谓的感染标志其实就是在PE文件中无关紧要的位置写入的一个字符串,所以感染标志的添加、读取与判断操作,其实就是基本的文件读写操作。

 

二、感染标志的添加

        在PE文件结构中存在着许多不实用的字段,比如在IMAGE_DOS_HEADER中,只有e_magic与e_lfanew这两个字段才是重要的,前者用于验证本文件是否为PE文件,后者保存着PE文件的偏移位置。因此可以从第二个字段,即e_cblp(Bytes on last page of file)开始,写入我们的感染标志。这里将该标志设定为“Hack”这四个字符。需要注意的是,将感染标志添加到文件中,应当首先将“Hack”转化为十六进制数值,然后再反向写入(小端显示),代码如下:

#define VIRUSFLAG 0x6b636148         // 感染标志,这里为“Hack”
// 感染标志的写入。三个参数分别为:欲感染文件的句柄、欲写入感染标志的位置
// 以及感染标志
BOOL AddSig(HANDLE hFile, DWORD dwAddr, DWORD dwSig)
{
    DWORD dwNum = 0;
    // 在文件中设置读写位置
    SetFilePointer(hFile, dwAddr, 0, FILE_BEGIN);
    // 写入感染标志
    if(WriteFile(hFile, &dwSig, sizeof(DWORD), &dwNum, NULL))
    {
        MessageBox(NULL, "感染标志添加成功!", "提示", MB_OK);
        return TRUE;
    }
    else
    {
        MessageBox(NULL, "感染标志添加失败!", "提示", MB_OK);
        return FALSE;
    }
}
        然后编写检测感染标志的代码:
// 感染标志检测
BOOL CheckSig(HANDLE hFile, DWORD dwAddr, DWORD dwSig)
{
    DWORD dwSigNum = 0;
    DWORD dwNum = 0;
	
    SetFilePointer(hFile, dwAddr, 0, FILE_BEGIN);
    ReadFile(hFile, &dwSigNum, sizeof(DWORD), &dwNum, NULL);

    if(dwSigNum == dwSig)
    {
        return TRUE;
    }

    return FALSE;
}
        我们需要令“病毒”程序在每次感染前先调用CheckSig()函数,依据其返回值来判断目标文件是否已经被感染过,然后再决定是否需要进行感染。主函数代码如下:
#include <windows.h>
#define FILENAME "helloworld.exe"    // 欲添加感染标志的文件名
#define offsetof(struct_t,member) (size_t)&(((struct_t *)0)-> member)
int main()
{
    HANDLE hFile = NULL;
    hFile = CreateFile(FILENAME,
                       GENERIC_READ | GENERIC_WRITE,
                       FILE_SHARE_READ,
                       NULL,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL,
                       NULL);
    if(CheckSig(hFile, offsetof(IMAGE_DOS_HEADER, e_cblp), VIRUSFLAG))
    {
        MessageBox(NULL, "本文件已经被感染过!", "提示", MB_OK);
        return -1;
    }
    AddSig(hFile, offsetof(IMAGE_DOS_HEADER, e_cblp), VIRUSFLAG);
	
    return 0;
}

        程序将感染标志写入了IMAGE_DOS_HEADER中的e_cblp位置,它不会对程序的执行产生任何影响。这里需要说明的是,程序中我们使用了offsetof()这个宏,它本来是被定义在stddef.h中的,这里我将其拿出来专门定义。这个宏的作用是求某个结构体的特定成员在结构体里面的偏移量,对于本程序来说就是求e_cblp在IMAGE_DOS_HEADER中的偏移,也就是2(因为它之前的e_magic占用了两个字节)。

        这里给大家分析一下(size_t)&(((struct_t*)0)-> member)的意义。首先,(struct_t *)0是一个指向struct_t类型(本程序中为IMAGE_DOS_HEADER)的指针,其指针值为 0,所以其作用就是把从地址 0 开始的存储空间映射为一个struct_t类型的对象。((struct_t *)0)-> member是访问类型中的成员member(本程序中为e_cblp),相应地 &((struct_t *)0)-> member) 就是返回这个成员的地址。由于对象的起始地址为 0,所以成员的地址其实就是相对于对象首地址的成员的偏移地址。最后再通过类型转换,转换为 size_t 类型(32位是unsigned int,64位是long unsigned int)。

 

三、程序测试

        为了测试我们的程序,这里依旧使用《反病毒攻防研究第004篇:利用缝隙实现代码的植入》中所编写的“helloworld.exe”程序。将两个程序放在同一个目录下,感染前先用Hex Editor Neo查看一下“helloworld.exe”程序的DOS头部分:

图1 感染前的DOS头

        然后运行本程序,再次查看DOS头部:


图2 感染后的DOS头

        可见,我们的感染是成功的。

 

四、小结

        给文件添加感染标志对于文件来说不会产生任何影响,很多感染型病毒都会给目标文件添加感染标志,比如“熊猫烧香”就会在程序中添加“WhBoy”标志。因此当时李俊所编写的“熊猫烧香”病毒专杀工具(李俊版)就是通过检测文件中是否有“WhBoy”标志来判断文件是否被感染,但是这种检测方法却过于粗糙了。

反病毒攻防研究第015篇:病毒感染标志的添加