網絡安全編程:注冊表下啟動項的管理
對于Windows操作系統來說,注冊表中保存了非常多的系統配置,例如常見的IE主頁保存在HKEY_LOCAL_MACHINE\Software\Mircosoft\Internet Explorer\Main下的Start Page中;再比如禁止磁盤驅動器自動運行的AutoRun功能在注冊表的HKEY_CURRENT_ USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer下的NoDriveTypeAutoRun中進行設置;還有映像劫持、文件關聯等很多系統配置,都可以在注冊表中直接進行配置。
有很多常見的安全工具都需要對注冊表進行操作,這里介紹通過注冊表獲得隨Windows系統啟動時的啟動項。在注冊表的啟動項中,除了正常的系統工具、軟件工具外,病毒和木馬也會利用注冊表的啟動項悄然地讓自己跟隨Windows的啟動而啟動,從而實現自啟動的功能。下面通過編寫一個枚舉注冊表啟動項的工具,進一步學習注冊表操作時使用API函數的相關流程。
1. 程序的界面及相關代碼
注冊表中可以用來完成開機啟動的地方非常多,這里只介紹注冊表中眾多可以完成開機啟動的其中一個位置。這里的程序使用對話框的形式,其界面如圖1所示。
圖1 注冊表啟動項管理界面
這個界面中用到了CListCtrl控件,用戶對其進行添加并進行相應的設置即可。這里給出一個關于CListCtrl初始化的代碼,具體如下:
- VOID CManageRunDlg::InitRunList()
- {
- // 設置擴展樣式
- m_RunList.SetExtendedStyle(
- m_RunList.GetExtendedStyle()
- | LVS_EX_GRIDLINES // 有網格
- | LVS_EX_FULLROWSELECT); // 選擇單行
- // 在 ListCtrl 中插入新列
- m_RunList.InsertColumn(0, "NO.");
- m_RunList.InsertColumn(1, "鍵值名稱");
- m_RunList.InsertColumn(2, "鍵 值");
- /*
- LVSCW_AUTOSIZE_USEHEADER:
- 列的寬度自動匹配為標題文本
- 如果這個值用在最后一列,列寬被設置為 ListCtrl 剩余的長度
- */
- m_RunList.SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER);
- m_RunList.SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER);
- m_RunList.SetColumnWidth(2, LVSCW_AUTOSIZE_USEHEADER);
- }
2. 啟動項的枚舉
這個實例主要是通過枚舉注冊表中的“HKEY_LOCAL_MACHINE\Software\Microso ft\Windows\CurrentVersion\Run”子鍵下的鍵值項,取得跟隨Windows啟動而啟動的程序。在運行軟件“注冊表啟動項管理”后,應該將上述注冊表子鍵位置下的所有啟動項的內容顯示出來,其代碼如下:
- #define REG_RUN "Software\\Microsoft\\Windows\\CurrentVersion\\Run\\"
- VOID CManageRunDlg::ShowRunList()
- {
- // 清空 ListCtrl 中的所有項
- m_RunList.DeleteAllItems();
- DWORD dwType = 0;
- DWORD dwBufferSize = MAXBYTE;
- DWORD dwKeySize = MAXBYTE;
- char szValueName[MAXBYTE] = { 0 };
- char szValueKey[MAXBYTE] = { 0 };
- HKEY hKey = NULL;
- LONG lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
- REG_RUN, 0, KEY_ALL_ACCESS, &hKey);
- if ( lRet != ERROR_SUCCESS )
- {
- return ;
- }
- int i = 0;
- CString strTmp;
- while ( TRUE )
- {
- // 枚舉鍵項
- lRet = RegEnumValue(hKey, i, szValueName,
- &dwBufferSize, NULL, &dwType,
- (unsigned char *)szValueKey, &dwKeySize);
- // 沒有則退出循環
- if ( lRet == ERROR_NO_MORE_ITEMS )
- {
- break;
- }
- // 顯示到列表控件中
- strTmp.Format("%d", i);
- m_RunList.InsertItem(i, strTmp);
- m_RunList.SetItemText(i, 1, szValueName);
- m_RunList.SetItemText(i, 2, szValueKey);
- ZeroMemory(szValueKey, MAXBYTE);
- ZeroMemory(szValueName, MAXBYTE);
- dwBufferSize = MAXBYTE;
- dwKeySize = MAXBYTE;
- i ++;
- }
- RegCloseKey(hKey);
- }
當將注冊表中的自啟動項顯示出來后,必然會對其進行一定的操作或處理。對于注冊表啟動項的管理來說,常見的有3個功能,首先是屏蔽啟動項,然后是刪除啟動項,最后是添加啟動項(這三者是并列關系,不是先后順序)。這里的程序中只完成后兩個功能,即刪除啟動項和添加啟動項。刪除啟動項和屏蔽啟動項是有差別的,其差別在于屏蔽啟動項是可恢復的,而刪除啟動項是不可恢復的,至于屏蔽啟動項這個功能就留給大家實現了。
3. 添加啟動項的代碼
只要將需要跟隨Windows啟動的軟件添加至“HKEY_LOCAL_MACHINE\Software\Micro soft\Windows\CurrentVersion\Run”子鍵下,就可以實現所需的功能,代碼如下:
- void CManageRunDlg::OnBtnAdd()
- {
- // TODO: Add your control notification handler code here
- CRegAdd RegAdd;
- RegAdd.DoModal();
- // 判斷輸入是否完整
- if ( strlen(RegAdd.m_szKeyName) > 0 &&
- strlen(RegAdd.m_szKeyValue) > 0)
- {
- HKEY hKey = NULL;
- LONG lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
- REG_RUN, 0, KEY_ALL_ACCESS, &hKey);
- if ( lRet != ERROR_SUCCESS )
- {
- return ;
- }
- RegSetValueEx(hKey, RegAdd.m_szKeyName, 0,
- REG_SZ, (const unsigned char*)RegAdd.m_szKeyValue,
- strlen(RegAdd.m_szKeyValue) + sizeof(char));
- RegCloseKey(hKey);
- ShowRunList();
- }
- else
- {
- AfxMessageBox("請輸入完整的內容");
- }
- }
在代碼中,CRegAdd對應著添加啟動項的窗口,該窗口的代碼如下:
- void CRegAdd::OnBtnOk()
- {
- // TODO: Add your control notification handler code here
- ZeroMemory(m_szKeyName, MAXBYTE);
- ZeroMemory(m_szKeyValue, MAX_PATH);
- GetDlgItemText(IDC_EDIT_KEYNAME, m_szKeyName, MAXBYTE);
- GetDlgItemText(IDC_EDIT_KEYVALUE, m_szKeyValue, MAX_PATH);
- EndDialog(0);
- }
4. 刪除啟動項的代碼
刪除啟動項的實現代碼比添加啟動項的代碼要簡單,但是在刪除的時候涉及一個關于CListCtrl控件的編程,也就是選中列表框中的哪個啟動項要進行刪除。這是一個對控件進行編程的問題,在代碼中獲取選中的啟動項后,要進行刪除就非常簡單了,代碼如下:
- void CManageRunDlg::OnBtnDel()
- {
- // 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);
- HKEY hKey = NULL;
- LONG lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
- REG_RUN, 0, KEY_ALL_ACCESS, &hKey);
- RegDeleteValue(hKey, szKeyName);
- RegCloseKey(hKey);
- ShowRunList();
- }
對于注冊表啟動項的管理軟件就編寫到這里,大家可以將其他的可以讓軟件開機啟動的注冊表子鍵添加到軟件中去,這樣啟動項管理軟件就更加強大、更加完美了。但是,當不斷深入對注冊表的了解時,會發現更多的可以讓軟件隨機啟動的子鍵,這樣就需要每次將新發現的子鍵添加到代碼中,而每次改動代碼是非常繁瑣的。那么,有沒有什么好的方法可以在每次添加子鍵的同時不改變代碼本身呢?可以把要枚舉的注冊表子鍵保存到一個文件中,然后讓程序去該文件中讀取這些子鍵,最后通過API函數對注冊表進行枚舉。這樣,以后每當在注冊表中有新的需要枚舉的內容時,只需要修改保存注冊表子鍵的文件即可,而不需要對程序本身進行修改了。