• 安全类工具制作第003篇:注册表启动项管理器


    一、前言

            注册表是Windows操作系统中的一个非常重要的数据库,里面记录了我们系统的几乎所有信息。也正因为这个原因,病毒木马也常常利用注册表来做文章。而对于杀毒软件来说,注册表也是要重点关注的对象。我在《反病毒攻防研究第002篇:利用注册表实现自启动》中也提到过,恶意程序可以利用注册表实现自启动,比如可以增加系统的启动项或通过映像劫持等的方法。这次我打算编写一个注册表启动项管理器,利用它可以对注册表的启动项进行查看,还可以删除可疑启动项。尽管功能很简单,但却也是反病毒木马的利器。需要说明的是,这篇文章所编写的程序,仅仅针对《利用注册表实现自启动》中提到的两个实现程序自启动的注册表键以及键值来讨论的,也就是说我这里并不会在程序中添加所有的注册表启动项,有兴趣的读者可以自行添加。

     

    二、界面的制作

            依旧利用MFC创建一个基于对话框的程序,界面如下:


    图1 界面设计

            程序需要使用两个“Static Text”、两个“List Control”以及三个“Button”控件。需要将两个“List Control”控件的属性都进行如下设置:


    图2 设置List Control的属性

            最后为界面上面的“List Control”控件添加一个名为“m_RunList”的变量,为下面的“List Control”控件添加一个名为“m_IFEOList”的变量。然后通过编程对两个“List Control”控件进行初始化,主要是对表格的属性进行设置:

    void CRegManageRunDlg::InitRunList()
    {
            //设置“List Control”控件的扩展风格
            m_RunList.SetExtendedStyle(
                    m_RunList.GetExtendedStyle()
                    | LVS_EX_GRIDLINES        //有网络格
                    | LVS_EX_FULLROWSELECT);  //选中某行使整行高亮(只适用于report风格)
    
            //添加列目
            m_RunList.InsertColumn(0, _T("序号"));
            m_RunList.InsertColumn(1, _T("名    称"));
            m_RunList.InsertColumn(2, _T("类    型"));
            m_RunList.InsertColumn(3, _T("数    据"));
            //设置列的宽度
            m_RunList.SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER);
            m_RunList.SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER);
            m_RunList.SetColumnWidth(2, LVSCW_AUTOSIZE_USEHEADER);
            m_RunList.SetColumnWidth(3, LVSCW_AUTOSIZE_USEHEADER);
    }
    
    void CRegManageRunDlg::InitIFEOList()
    {
            //设置“List Control”控件的扩展风格
            m_IFEOList.SetExtendedStyle(
                    m_IFEOList.GetExtendedStyle()
                    | LVS_EX_GRIDLINES         //有网络格
                    | LVS_EX_FULLROWSELECT);   //选中某行使整行高亮(只适用于report风格)
            //添加列目
            m_IFEOList.InsertColumn(0, _T("序号"));
            m_IFEOList.InsertColumn(1, _T("被劫持程序名称"));
            //设置列的宽度
            m_IFEOList.SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER);
            m_IFEOList.SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER);
    }
            然后进行对话框的初始化,在OnInitDialog()中添加:
    InitRunList();	
    InitIFEOList();
            最后在头文件中的相应位置添加:
    void InitRunList();	
    void InitIFEOList();

            我之所以要使用两个“ListControl”控件,是因为我需要枚举启动项和映像劫持。前者所枚举的是启动项“Run”这个“键”下面的所有“键值”,而后者所枚举的是“映像劫持”这个“键”下面的所有“子键”。对于枚举不同的内容,我觉得还是分开讨论比较好。

     

    三、编写代码

            首先需要编写两个宏定义,表示我们将要枚举的项:
    #define REG_RUN  "Software\Microsoft\Windows\CurrentVersion\Run\"
    #define REG_IFEO "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\"
            接下来编写显示启动项的代码:

    void CRegManageRunDlg::ShowRunList()
    {
            //清空列表
            m_RunList.DeleteAllItems();
    
            DWORD dwType = 0;
            DWORD dwBufferSize = MAXBYTE;
            DWORD dwKeySize = MAXBYTE;
        
            char szValueName[MAXBYTE] = { 0 };
            char szValueKey[MAXBYTE] = { 0 };
            char szType[MAXBYTE] = { 0 };
       
            //打开注册表启动项
            HKEY hKeyRun = NULL;
            LONG lRetRun = RegOpenKey(HKEY_CURRENT_USER, REG_RUN, &hKeyRun);
            if ( lRetRun != ERROR_SUCCESS )
            {
                    AfxMessageBox("注册表打开失败!");
                    return ;
            } 
    
            int i = 0;	
            CString strRunTmp;
    
            while ( TRUE )
            {     
                    lRetRun = RegEnumValue(hKeyRun, i, szValueName, &dwBufferSize, NULL, &dwType, (unsigned char *)szValueKey, &dwKeySize);
                    if ( lRetRun == ERROR_NO_MORE_ITEMS )
                    {
                            break;
                    } 
                    strRunTmp.Format("%d", i);      
                    //判断注册表项值的类型
                    if(dwType == REG_SZ)
                    {
                            //一个以0结尾的字符串
                            strcpy(szType, "REG_SZ");
                    }
                    else if(dwType == REG_BINARY)
                    {
                            //任何形式的二进制数据
                            strcpy(szType, "REG_BINARY");
                    }
                    else if(dwType == REG_DWORD)
                    {
                            //一个32位的数字
                            strcpy(szType, "REG_DWORD");
                    }
                    else if(dwType == REG_EXPAND_SZ)
                    {
                            //一个以0结尾的字符串,该字符串包含对环境变量(如%PATH%)的未扩展引用
                            strcpy(szType, "REG_EXPAND_SZ");
                    }
                    else
                    {
                            //其它
                            strcpy(szType, "OTHER");
                    }
                    //将获取的注册表内容写入“List Control”控件      
                    m_RunList.InsertItem(i, strRunTmp);		
                    m_RunList.SetItemText(i, 1, szValueName);
                    m_RunList.SetItemText(i, 2, szType);
                    m_RunList.SetItemText(i, 3, szValueKey);
            
                    i++;
            
                    //清空缓冲区
    		ZeroMemory(szValueName,MAXBYTE);
                    ZeroMemory(szType,MAXBYTE);
                    ZeroMemory(szValueKey, MAXBYTE);				
            }
            RegCloseKey(hKeyRun);
    }
            然后编写显示映像劫持的代码:
    void CRegManageRunDlg::ShowIFEOList()
    {
            //清空列表
            m_IFEOList.DeleteAllItems();
        
            DWORD dwSize = MAXBYTE;
            char szKeyName[MAXBYTE] = { 0 };
    	
            //打开注册表映像劫持项
            HKEY hKeyIFEO = NULL;
            LONG lRetIFEO = RegOpenKey(HKEY_LOCAL_MACHINE, REG_IFEO, &hKeyIFEO);
            if ( lRetIFEO != ERROR_SUCCESS )
            {
                    AfxMessageBox("映像劫持打开失败!");
                    return ;
            }
    	
            int j = 0;
            CString strTmp;
    	
            while(TRUE)
            {
                    strTmp.Format("%d", j);	
                    lRetIFEO = RegEnumKey(hKeyIFEO, j, szKeyName, dwSize);
                    if ( lRetIFEO == ERROR_NO_MORE_ITEMS )
                    {
                            break;
                    }
           
                    m_IFEOList.InsertItem(j, strTmp);		
                    m_IFEOList.SetItemText(j, 1, szKeyName);      
    		
                    j ++;
    		
                    ZeroMemory(szKeyName,MAXBYTE);       
            }
            RegCloseKey(hKeyIFEO);
    }
            为了使这两个函数正常使用,需要在对话框初始化时,就将注册表内容显示出来。在OnInitDialog()中添加:
    ShowRunList();
    ShowIFEOList();
            再在头文件中进行函数声明:
    void ShowIFEOList();
    void ShowRunList();
            至此,两个“List Control”控件的程序编写完毕,接下来是三个按钮控件的编程,首先是“删除启动项”按钮:
    void CRegManageRunDlg::OnBtnRunDel() 
    {
            // TODO: Add your control notification handler code here
            POSITION pos = m_RunList.GetFirstSelectedItemPosition();
            int nSelected = -1;
    
            while ( pos )
            {
                    nSelected = m_RunList.GetNextSelectedItem(pos);
            }
    
            if ( -1 == nSelected )
            {
                    AfxMessageBox("您尚未选择选择要删除的启动项!");
                    return ;
            }
    
            char szKeyName[MAXBYTE] = { 0 };
            m_RunList.GetItemText(nSelected, 1, szKeyName, MAXBYTE);
    
            CString str = "您即将要删除的启动项是:"; 
            str += szKeyName;
            AfxMessageBox(str);
            str.Empty();
    
            HKEY hKey = NULL;
            LONG lRet = RegOpenKey(HKEY_CURRENT_USER, REG_RUN, &hKey);
            if ( lRet != ERROR_SUCCESS )
            {
                    AfxMessageBox("启动项打开失败!");
                    return ;
            }
       
            RegDeleteValue(hKey, szKeyName);
            RegCloseKey(hKey);
        
            ShowRunList();
    }
            然后是“删除映像劫持”按钮:
    void CRegManageRunDlg::OnBtnIFEODel() 
    {
            // TODO: Add your control notification handler code here
            POSITION pos = m_IFEOList.GetFirstSelectedItemPosition();
            int nSelected = -1;
    
            while ( pos )
            {
                    nSelected = m_IFEOList.GetNextSelectedItem(pos);
            }
    
            if ( -1 == nSelected )
            {
                    AfxMessageBox("您尚未选择选择要删除的映像劫持!");
                    return ;
            }
    
            char szKeyName[MAXBYTE] = { 0 };
            m_IFEOList.GetItemText(nSelected, 1, szKeyName, MAXBYTE);
    
            CString str = "您即将要删除的映像劫持是:"; 
            str += szKeyName;
            AfxMessageBox(str);
            str.Empty();
        
            HKEY hKey = NULL;
            LONG lRet = RegOpenKey(HKEY_LOCAL_MACHINE, REG_IFEO, &hKey);
            if ( lRet != ERROR_SUCCESS )
            {
                    AfxMessageBox("映像劫持打开失败!");
                    return ;
            }
        
            RegDeleteKey(hKey, szKeyName);
            RegCloseKey(hKey);
        
            ShowIFEOList();
    }
            最后是“退出”按钮:
    void CRegManageRunDlg::OnBtnQuit() 
    {
            // TODO: Add your control notification handler code here
            EndDialog(0);
    }

            至此,所有代码编写完毕。由于程序原理非常简单,所以这里无需过多论述。

     

    四、实际测试

            测试时需要使用《反病毒攻防研究第002篇:利用注册表实现自启动》中编写的用于模拟计算机病毒的程序,它会在注册表中的两个位置添加自身,以实现自启动。运行该“病毒”程序后,用这里编写的“启动项管理器”进行查看:

    图3 查看启动项

            可见我们的程序已经正确识别出了“恶意程序”所添加的启动项,接下来就可以通过点击相应的按钮进行删除,这里不再赘述。

    五、小结

            我们可以在此基础上开发一个完整的注册表启动项管理器,使其可以枚举所有的启动项,再添加其它一些实用的功能,令恶意程序无处藏身。MFC为我们提供了丰富的控件,利用它们可以编写出功能强大且界面精美的程序,这也是我的《安全类软件编写》这一系列坚持使用MFC的原因。
  • 相关阅读:
    C# 委托事件
    用netstat查看网络状态详解
    详解TCP建立连接全过程
    Amazon SNS移动推送更新——新增百度云推送和Windows平台支持
    UE-9260使用说明2
    简单理解javascript中的原型对象,实现对之间共享属性和行为
    RxJava
    链式存储(头插法、尾插法)
    Codeforces 569 B. Inventory
    CentOS 6.4安装Puppet
  • 原文地址:https://www.cnblogs.com/csnd/p/11785802.html
Copyright © 2020-2023  润新知