熊猫烧香之专杀工具的书写

来源:互联网 发布:java ftpclient 卡住 编辑:程序博客网 时间:2024/05/01 14:12

实验环境

  • 实验环境
    • 操作机: Windows XP
  • 实验工具:
ToolsPathNotepad++C:\Notepad++Process MonitorC:\Tools\ProcessMonitor
  • 实验文件:
    • setup.exe
    • KillWhBoy.exe

结合本篇文章的知识点,能够彻底掌握文章所讲述的编写杀毒软件的方法。

实验步骤

第一步 下载实验文件

请访问http://file.ichunqiu.com/e0c74428下载实验文件

小i提示

  • 在本次实验中,请注意实验工具、实验文件存放路径,不同的文件路径可能会出现不一样的实验结果。
  • 在实验环境中无法连接互联网,请使用您本地的网络环境。

快速查找实验工具

  • 打开桌面 Everything 搜索工具,输入实验工具名称,右击选择“打开路径”,跳转实验工具所在位置。
  • 以查找BURP为例为大家演示。

Alt text

第二步 病毒行为回顾与归纳

这里我们首先回顾一下病毒的行为:

病毒行为1:病毒本身创建了名为spoclsv.exe的进程,该进程文件的路径为:

C:\WINDOWS\system32\drivers\spoclsv.exe

病毒行为2:在命令行模式下使用net share命令来取消系统中的共享。

病毒行为3:删除安全类软件在注册表中的启动项。

病毒行为4:在注册表HKCU\Software\Microsoft\Windows\CurrentVersion\Run中创建svcshare,用于在开机时启动位于C:\WINDOWS\system32\drivers\spoclsv.exe的病毒程序。

病毒行为5:修改注册表,使得隐藏文件无法通过普通的设置进行显示,该位置为:

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersio\nExplorer\Advanced\Folder\Hidden\SHOWALL

病毒将CheckedValue的键值设置为了0。

病毒行为6:将自身拷贝到根目录,并命名为setup.exe,同时创建autorun.inf用于病毒的启动,这两个文件的属性都是“隐藏”。

病毒行为7:在一些目录中创建名为Desktop_.ini的隐藏文件。

病毒行为8:向外发包,连接局域网中其他机器。

纵观以上八点行为,这里需要说明的是,其中的第二点行为,由于我不知道用户计算机在中毒前的设置,因此这条我打算忽略。第三点行为,我不知道用户的计算机安装了哪些杀毒软件,而病毒又会将所有杀软的注册表启动项删除,所以这一条我打算忽略,用户在使用本专杀工具后,可以自行重新安装杀软,或者有经验的用户也可以自行在注册表中添加回杀软名称。另外,病毒的第八点行为,我也打算忽略,因为只要删除了病毒本体,那么自然就解决了这个问题,所以我的专杀工具主要应付剩下的五个问题。在这里各位读者想必也能够发现,我的专杀工具所要做的工作,与我之前写的手动查杀的过程其实是极为相似的,这也是为什么我当时就强调,我们依旧要掌握手动查杀病毒这个技能的原因。

第三步 专杀工具界面的制作

如果使用批处理来杀毒,因为它运行时没有界面,因此我们往往不知道杀毒程序究竟干了些什么,也不知道究竟有没有查杀成功,这也凸显了使用高级语言开发专杀工具的优势。这里我使用MFC进行“熊猫烧香”病毒专杀工具的开发,绘制界面如下图所示:

Alt text

其中的“Edit Box”控件的属性调整如下:

Alt text

界面非常简单,接下来就是代码的编写。

第四步 计算病毒程序的散列值

在查杀病毒的技术中有一种方法类似于特征码查杀法,这种方法并不从病毒内提取特征码,而是计算病毒的散列值。利用这个散列值,就可以在查杀的过程中计算每个文件的散列,然后进行比较。这种方法简单易于实现,一般在病毒刚被发现时,在逆向分析前使用。常见的计算散列的算法有MD5、Sha-1以及CRC32等。 这里使用CRC32算法计算散列值,代码如下:

DWORD CRC32(BYTE* ptr,DWORD Size){    DWORD crcTable[256],crcTmp1;    //动态生成CRC-32表    for (int i=0; i<256; i++)    {        crcTmp1 = i;        for (int j=8; j>0; j--)        {            if (crcTmp1&1) crcTmp1 = (crcTmp1 >> 1) ^ 0xEDB88320L;            else crcTmp1 >>= 1;        }        crcTable[i] = crcTmp1;    }    //计算CRC32值    DWORD crcTmp2= 0xFFFFFFFF;    while(Size--)    {        crcTmp2 = ((crcTmp2>>8) & 0x00FFFFFF) ^ crcTable[ (crcTmp2^(*ptr)) & 0xFF ];        ptr++;    }    return (crcTmp2^0xFFFFFFFF);}

该函数的参数有两个,一个是指向缓冲区的指针,第二个是缓冲区的长度。它将文件全部读入缓冲区中,然后用CRC32函数计算文件的CRC32散列值,可以得到我所研究的“熊猫烧香”病毒的散列值为0x89240FCD。这里请大家注意,不同版本的病毒的散列值是不同的,我所得出的这个值仅针对我所讨论的这个版本的病毒。

第五步 查找进程与提升权限

我们需要在内存中查找病毒是否存在,代码如下:

BOOL FindTargetProcess(char *pszProcessName,DWORD *dwPid){    BOOL bFind = FALSE;    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);    if (hProcessSnap == INVALID_HANDLE_VALUE)    {        return bFind;    }    PROCESSENTRY32 pe = { 0 };    pe.dwSize = sizeof(pe);    BOOL bRet = Process32First(hProcessSnap,&pe;);    while (bRet)    {        if (lstrcmp(pe.szExeFile,pszProcessName) == 0)        {            *dwPid = pe.th32ProcessID;            bFind = TRUE;            break;        }        bRet = Process32Next(hProcessSnap,&pe;);    }    CloseHandle(hProcessSnap);    return bFind;}

这里还需要提升系统的权限,提升成功后,当前进程就可以访问一些受限的系统资源。代码如下:

BOOL EnableDebugPrivilege(char *pszPrivilege){    HANDLE hToken = INVALID_HANDLE_VALUE;    LUID luid;    TOKEN_PRIVILEGES tp;    BOOL bRet = OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hToken;);    if (bRet == FALSE)    {        return bRet;    }    bRet = LookupPrivilegeValue(NULL,pszPrivilege,&luid;);    if (bRet == FALSE)    {        return bRet;    }    tp.PrivilegeCount = 1;    tp.Privileges[0].Luid = luid;    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;    bRet = AdjustTokenPrivileges(hToken,FALSE,&tp;,sizeof(tp),NULL,NULL);    return bRet;}

第六步 查找并删除Desktop_.ini文件

病毒会在所有盘符下面的非系统目录中创建名为Desktop_.ini的文件,虽说这个文件看似并不会对系统产生什么危害,但是为了实现对“熊猫烧香”的彻底查杀,还是应当将其删除的。这里主要涉及两方面的知识,一个是遍历整个磁盘的文件,这需要使用FindFirstFile()与FindNextFile()这两个API函数,并采用递归调用的方法;另一个是修改文件属性,因为病毒创建出来的文件会带有系统、只读和隐藏这三个属性,若不对其进行更改,是无法删除病毒文件的。依照这个思想,编写代码如下:

DWORD WINAPI FindFiles(LPVOID lpszPath){ WIN32_FIND_DATA stFindFile; HANDLE hFindFile; // 扫描路径 char szPath[MAX_PATH];     char szFindFile[MAX_PATH]; char szSearch[MAX_PATH]; char *szFilter; int len; int ret = 0; szFilter = "*.*"; lstrcpy(szPath, (char *)lpszPath); len = lstrlen(szPath); if(szPath[len-1] != '\\') {  szPath[len] = '\\';  szPath[len+1] = '\0'; } lstrcpy(szSearch, szPath); lstrcat(szSearch,szFilter); hFindFile = FindFirstFile(szSearch, &stFindFile;); if(hFindFile != INVALID_HANDLE_VALUE) {     do  {      lstrcpy(szFindFile, szPath);            lstrcat(szFindFile, stFindFile.cFileName);   if(stFindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)   {       if(stFindFile.cFileName[0] != '.')    {        FindFiles(szFindFile);    }   }   else   {    if(!lstrcmp(stFindFile.cFileName,"Desktop_.ini"))    {        // 去除文件的隐藏、系统以及只读属性     DWORD dwFileAttributes = GetFileAttributes(szFindFile);                    dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;                    dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM;                    dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;     SetFileAttributes(szFindFile, dwFileAttributes);                    // 删除Desktop_.ini              BOOL bRet = DeleteFile(szFindFile);      csTxt += szFindFile;     if (bRet)     {                        csTxt += _T("被删除!\r\n");     }                     else     {                        csTxt += _T("无法删除\r\n");     }    }   }   ret = FindNextFile(hFindFile, &stFindFile;);  }while(ret != 0); } FindClose(hFindFile); return 0;}

需要说明的是,这里需要在本程序前定义一个CString类型的csTxt全局变量,用于将查杀的结果信息输出到程序界面,之后的程序中也会用到这个变量。

第七步 主程序的编写

主程序也就是“一键查杀”按钮的响应,我在代码中已添加相应的注释:

void CKillWhBoyDlg::OnBtnKill() { // TODO: Add your control notification handler code here BOOL bRet = FALSE;    DWORD dwPid = 0; /////////////////////////////////////////////////////////////////////  结束spoclsv.exe进程,并删除病毒程序本身///////////////////////////////////////////////////////////////////    bRet = FindTargetProcess("spoclsv.exe", &dwPid;);    if (bRet == TRUE)    {        csTxt = _T("检查系统内存...");        csTxt += _T("系统中存在病毒进程:spoclsv.exe");        csTxt += _T("准备进行查杀...");        SetDlgItemText(IDC_LIST,csTxt);           // 提升权限     bRet = EnableDebugPrivilege(SE_DEBUG_NAME);        if (bRet == FALSE)  {            csTxt += _T("提升权限失败");  }        else  {            csTxt += _T("提升权限成功!");  }     SetDlgItemText(IDC_LIST,csTxt);  // 打开并尝试结束病毒进程  HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPid);        if (hProcess == INVALID_HANDLE_VALUE)        {            csTxt += _T("无法结束病毒进程");            return ;        }  bRet = TerminateProcess(hProcess,0);        if (bRet == FALSE)        {            csTxt += _T("无法结束病毒进程");            return ;        }        csTxt += _T("病毒进程已经结束");        SetDlgItemText(IDC_LIST,csTxt);        CloseHandle(hProcess);    }    else    {        csTxt += _T("系统中不存在spoclsv.exe病毒进程");    }    Sleep(10);    // 查杀磁盘中是否存在名为spoclsv.exe的病毒文件    char szSysPath[MAX_PATH] = { 0 };    GetSystemDirectory(szSysPath,MAX_PATH);    lstrcat(szSysPath,"\drivers\spoclsv.exe");    csTxt += _T("检查硬盘中是否存在spoclsv.exe文件...");    if (GetFileAttributes(szSysPath) == 0xFFFFFFFF)    {        csTxt += _T("spoclsv.exe病毒文件不存在");    }    else    {        csTxt += _T("spoclsv.exe病毒文件存在,正在计算散列值");        HANDLE hFile = CreateFile(szSysPath,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);        if (hFile == INVALID_HANDLE_VALUE)        {            AfxMessageBox("Create Error");            return ;        }        DWORD dwSize = GetFileSize(hFile,NULL);        if (dwSize == 0xFFFFFFFF)        {            AfxMessageBox("GetFileSize Error");            return ;        }        BYTE *pFile = (BYTE*)malloc(dwSize);        if (pFile == NULL)        {            AfxMessageBox("malloc Error");            return ;        }        DWORD dwNum = 0;        ReadFile(hFile,pFile,dwSize,&dwNum;,NULL);        // 计算spoclsv.exe的散列值        DWORD dwCrc32 = CRC32(pFile,dwSize);        if (pFile != NULL)        {            free(pFile);            pFile = NULL;        }        CloseHandle(hFile);        // 0x89240FCD是“熊猫烧香”病毒的散列值        if (dwCrc32 != 0x89240FCD)        {            csTxt += _T("spoclsv.exe校验和验证失败");        }        else        {            csTxt += _T("spoclsv.exe校验和验证成功,正在删除...");            // 去除文件的隐藏、系统以及只读属性   DWORD dwFileAttributes = GetFileAttributes(szSysPath);            dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;            dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM;            dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;   SetFileAttributes(szSysPath, dwFileAttributes);            // 删除spoclsv.exe   bRet = DeleteFile(szSysPath);            if (bRet)            {                csTxt += _T("spoclsv.exe病毒被删除!");            }             else            {                csTxt += _T("spoclsv.exe病毒无法删除");            }        }    }      SetDlgItemText(IDC_LIST,csTxt);    Sleep(10);/////////////////////////////////////////////////////////////////////  删除每个盘符下的setup.exe与autorun.inf,以及Desktop_.ini///////////////////////////////////////////////////////////////////    char szDriverString[MAXBYTE] = { 0 };      char *pTmp = NULL;      //获取字符串类型的驱动器列表      GetLogicalDriveStrings(MAXBYTE, szDriverString);      pTmp = szDriverString;      while( *pTmp )      {    char szAutorunPath[MAX_PATH] = { 0 };      char szSetupPath[MAX_PATH] = { 0 };        lstrcat(szAutorunPath,pTmp);  lstrcat(szAutorunPath,"autorun.inf");        lstrcat(szSetupPath,pTmp);  lstrcat(szSetupPath,"setup.exe");        if (GetFileAttributes(szSetupPath) == 0xFFFFFFFF)  {            csTxt += pTmp;   csTxt += _T("setup.exe病毒文件不存在");  }        else  {            csTxt += pTmp;   csTxt += _T("setup.exe病毒文件存在,正在进行计算校验和...");            HANDLE hFile = CreateFile(szSetupPath,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);            if (hFile == INVALID_HANDLE_VALUE)   {                AfxMessageBox("Create Error");                return ;   }            DWORD dwSize = GetFileSize(hFile,NULL);            if (dwSize == 0xFFFFFFFF)   {                AfxMessageBox("GetFileSize Error");                return ;   }            BYTE *pFile = (BYTE*)malloc(dwSize);            if (pFile == NULL)   {                AfxMessageBox("malloc Error");                return ;   }            DWORD dwNum = 0;            ReadFile(hFile,pFile,dwSize,&dwNum;,NULL);            DWORD dwCrc32 = CRC32(pFile,dwSize);               if (pFile != NULL)   {                free(pFile);                pFile = NULL;   }            CloseHandle(hFile);            if (dwCrc32 != 0x89240FCD)   {                csTxt += _T("校验和验证失败");   }            else   {                csTxt += _T("校验和验证成功,正在删除...");     // 去除文件的隐藏、系统以及只读属性    DWORD dwFileAttributes = GetFileAttributes(szSetupPath);                dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;                dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM;                dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;    SetFileAttributes(szSetupPath, dwFileAttributes);    // 删除setup.exe          bRet = DeleteFile(szSetupPath);                 if (bRet)    {                    csTxt += pTmp;     csTxt += _T("setup.exe病毒被删除!");    }                 else    {                    csTxt += pTmp;     csTxt += _T("setup.exe病毒无法删除");    }   }  }  // 去除文件的隐藏、系统以及只读属性  DWORD dwFileAttributes = GetFileAttributes(szAutorunPath);        dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;        dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM;        dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;  SetFileAttributes(szAutorunPath, dwFileAttributes);        // 删除autorun.inf  bRet = DeleteFile(szAutorunPath);         csTxt += pTmp;  if (bRet)  {            csTxt += _T("autorun.inf被删除!");  }         else  {   csTxt += _T("autorun.inf不存在或无法删除");  }  // 删除Desktop_.ini  FindFiles(pTmp);  // 检查下一个盘符        pTmp += 4;   }      Sleep(10);/////////////////////////////////////////////////////////////////////  修复注册表内容,删除病毒启动项并修复文件的隐藏显示///////////////////////////////////////////////////////////////////    csTxt += _T("正在检查注册表...");    SetDlgItemText(IDC_LIST,csTxt);    // 首先检查启动项    char RegRun[] = "Software\Microsoft\Windows\CurrentVersion\Run";       HKEY hKeyHKCU = NULL;        LONG lSize = MAXBYTE; char cData[MAXBYTE] = { 0 }; long lRet = RegOpenKey(HKEY_CURRENT_USER, RegRun, &hKeyHKCU;);    if(lRet == ERROR_SUCCESS)    {        lRet = RegQueryValueEx(hKeyHKCU,"svcshare",NULL,NULL,(unsigned char *)cData,(unsigned long *)&lSize;);        if ( lRet == ERROR_SUCCESS)        {            if (lstrcmp(cData,"C:\WINDOWS\system32\drivers\spoclsv.exe") == 0)            {                csTxt += _T("注册表启动项中存在病毒信息");            }            lRet = RegDeleteValue(hKeyHKCU,"svcshare");            if (lRet == ERROR_SUCCESS)            {                csTxt += _T("注册表启动项中的病毒信息已删除!");            }            else            {                csTxt += _T("注册表启动项中的病毒信息无法删除");            }        }        else        {            csTxt += _T("注册表启动项中不存在病毒信息");        }        RegCloseKey(hKeyHKCU);    }     else    {        csTxt += _T("注册表启动项信息读取失败");    }    // 接下来修复文件的隐藏显示,需要将CheckedValue的值设置为1    char RegHide[] = "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\Hidden\SHOWALL";     HKEY hKeyHKLM = NULL;  DWORD dwFlag = 1; long lRetHide = RegOpenKey(HKEY_LOCAL_MACHINE, RegHide, &hKeyHKLM;); if(lRetHide == ERROR_SUCCESS) {     csTxt += _T("检测注册表的文件隐藏选项...");  if( ERROR_SUCCESS == RegSetValueEx(       hKeyHKLM,             //subkey handle                  "CheckedValue",       //value name                  0,                    //must be zero                  REG_DWORD,            //value type                  (CONST BYTE*)&dwFlag;, //pointer to value data                  4))                   //length of value data  {      csTxt += _T("注册表修复完毕!");  }  else  {      csTxt += _T("无法恢复注册表的文件隐藏选项");  } }///////////////////////////////////////////////////////////////////// 病毒查杀完成/////////////////////////////////////////////////////////////////// csTxt += _T("病毒查杀完成,请使用专业杀毒软件进行全面扫描!");    SetDlgItemText(IDC_LIST,csTxt);}

至此,所有代码编写完毕,将其编译,并没有错误,直接生成可执行文件。

第八步 专杀工具测试

为了测试本专杀工具,我将它和“熊猫烧香”都拷贝到虚拟机中。首先运行病毒程序,之后再运行本专杀工具,如下图所示:

Alt text

可见,本程序已经将病毒彻底杀除,结合Process Monitor对本程序的监控,可以知道我们的专杀工具是切实可行的,这里不再赘述。

第九步 小结

  因为我们这次所面对的是一个实际的病毒,因此代码显得较长,这和我之前所举的例子中,仅仅几行代码就能够删除一个“虚拟”病毒是截然不同的。在以后面对不同病毒木马的专杀工具的编写中,我还会以这个专杀工具的代码为框架进行制作。而在以后的文章中,我只会对新增的知识点进行叙述,而与这篇文章重合的知识点,我就会一笔带过。也希望各位读者能够彻底掌握本篇文章所讲述的方法

.
0 0