## 一、前言
注冊表是Windows操作系統中的一個非常重要的數據庫,里面記錄了我們系統的幾乎所有信息。也正因為這個原因,病毒木馬也常常利用注冊表來做文章。而對于殺毒軟件來說,注冊表也是要重點關注的對象。我在[《反病毒攻防研究第002篇:利用注冊表實現自啟動》](http://blog.csdn.net/ioio_jy/article/details/38894059)中也提到過,惡意程序可以利用注冊表實現自啟動,比如可以增加系統的啟動項或通過映像劫持等的方法。這次我打算編寫一個注冊表啟動項管理器,利用它可以對注冊表的啟動項進行查看,還可以刪除可疑啟動項。盡管功能很簡單,但卻也是反病毒木馬的利器。需要說明的是,這篇文章所編寫的程序,僅僅針對[《利用注冊表實現自啟動》](http://blog.csdn.net/ioio_jy/article/details/38894059)中提到的兩個實現程序自啟動的注冊表鍵以及鍵值來討論的,也就是說我這里并不會在程序中添加所有的注冊表啟動項,有興趣的讀者可以自行添加。
## 二、界面的制作
依舊利用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篇:利用注冊表實現自啟動》](http://blog.csdn.net/ioio_jy/article/details/38894059)中編寫的用于模擬計算機病毒的程序,它會在注冊表中的兩個位置添加自身,以實現自啟動。運行該“病毒”程序后,用這里編寫的“啟動項管理器”進行查看:

圖3 查看啟動項
可見我們的程序已經正確識別出了“惡意程序”所添加的啟動項,接下來就可以通過點擊相應的按鈕進行刪除,這里不再贅述。
## 五、小結
我們可以在此基礎上開發一個完整的注冊表啟動項管理器,使其可以枚舉所有的啟動項,再添加其它一些實用的功能,令惡意程序無處藏身。MFC為我們提供了豐富的控件,利用它們可以編寫出功能強大且界面精美的程序,這也是我的《安全類軟件編寫》這一系列堅持使用MFC的原因。