## 一、前言
這次的程序是為了完善上一次所編寫的進程管理器。使得當我們選中某一個進程的時候,可以查看其DLL文件,并且能夠對可疑的模塊進行卸載操作。這樣就可以有效對抗DLL的惡意注入。
## 二、界面制作
這個界面是要依托于上一篇文章中制作的界面,需要單擊上次界面中的“查看DLL”按鈕來啟動。在上次的工作區中,找到VC6中菜單欄的“Insert”選項,在其下拉菜單中選擇“Resource…”,在彈出的界面中選擇“Dialog”然后單擊“New”,如下所示:

圖1 添加窗口
接下來需要為新窗口取一個名字,比如IDD_DIALOG_DLL。然后再為其添加一個新類,比如CDLLCheck。這樣就可以開始設計窗口了,如下圖所示:

圖2 界面設計
在這個新添加的窗口中,包含有這里需要一個“List Control”和兩個“Button”控件。接下來為列表框添加一個名為m_CheckDLL的變量,然后編寫代碼對其初始化:
~~~
void CDLLCheck::InitDLLList()
{
//設置“List Control”控件的擴展風格
m_CheckDLL.SetExtendedStyle(
m_CheckDLL.GetExtendedStyle()
| LVS_EX_GRIDLINES //有網絡格
| LVS_EX_FULLROWSELECT); //選中某行使整行高亮(只適用于report風格)
//添加列目
m_CheckDLL.InsertColumn(0, _T("序號"));
m_CheckDLL.InsertColumn(1, _T("名 稱"));
m_CheckDLL.InsertColumn(2, _T("路 徑"));
//設置列的寬度
m_CheckDLL.SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER);
m_CheckDLL.SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER);
m_CheckDLL.SetColumnWidth(2, LVSCW_AUTOSIZE_USEHEADER);
}
~~~
因為我希望在這個窗口剛被打開的時候,上述初始化代碼就能夠執行,但是這個新增加的窗口卻沒有OnInitDialog()這樣的初始化函數,所以需要手動添加。在VC6的菜單欄中選擇“View”,單擊下拉菜單中的“ClassWizard”,在“Message Map”選項卡中進行如下設置:

圖3 添加初始化函數
單擊OK后,新窗口的cpp程序中就出現了初始化函數:
~~~
BOOL CDLLCheck::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
~~~
然后再填入:
~~~
InitDLLList();
~~~
再于新窗口頭文件中寫入:
~~~
void InitDLLList();
~~~
## 三、編寫“查看DLL”按鈕控件代碼
這里所說的按鈕,指的是上一個窗口中的“查看DLL”按鈕控件。我希望在單擊這個按鈕之后,能夠彈出這次新建的IDD_DIALOG_DLL窗口,并且能夠直接顯示出所選擇進程的DLL。可以編寫程序打開一個模態對話框:
~~~
void CProcessManageDlg::OnBtnDLL()
{
// TODO: Add your control notification handler code here
pid = GetSelectPid();
CDLLCheck DLLCheck;
DLLCheck.DoModal();
}
~~~
上述代碼定義了一個對話框對象:DLLCheck,然后利用這個對象調用DoModal函數以產生一個模態對話框。因為主窗口并不知道這個CDLLCheck對話框是什么樣的數據類型,所以還必須在主窗口函數的源文件中包含CDLLCheck類的頭文件,即“DLLCheck.h”。
需要強調的是,因為我想要把主窗口中被選中進程的PID值傳入新窗口以查看其所包含的DLL文件,所以需要在主窗口頭文件中的CDialog下的public中聲明一個變量:
~~~
int pid;
~~~
這樣,子窗口就能夠調用父窗口中獲取的PID值了。所以上述程序的第一句就是先獲取所選中進程的PID值,再打開子窗口。
## 四、DLL的枚舉
DLL枚舉的代碼填寫在新窗口的源文件中,其原理與上篇文章討論的進程枚舉類似,不同的是這里需要先獲取父窗口中的PID值,代碼如下:
~~~
void CDLLCheck::ShowModule()
{
//清空列表
m_CheckDLL.DeleteAllItems();
//獲取父窗口中的公共變量(所選中進程的PID值)
CProcessManageDlg *p;
p = (CProcessManageDlg *) GetParent();
int nPid = p->pid;
MODULEENTRY32 Me32 = { 0 };
Me32.dwSize = sizeof(MODULEENTRY32);
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, nPid);
if ( hSnap == INVALID_HANDLE_VALUE )
{
AfxMessageBox("創建快照失敗!");
return ;
}
BOOL bRet = Module32First(hSnap, &Me32);
int i = 0;
CString str;
while ( bRet )
{
str.Format("%d", i);
m_CheckDLL.InsertItem(i, str);
m_CheckDLL.SetItemText(i, 1, Me32.szModule);
m_CheckDLL.SetItemText(i, 2, Me32.szExePath);
i ++;
bRet = Module32Next(hSnap, &Me32);
}
}
~~~
這個程序我也是希望在剛打開窗口的時候就顯示出來,因此需要在新對話框的OnInitDialog()中添加:
~~~
ShowMoudle();
~~~
并在頭文件添加:
~~~
void ShowModule();
~~~
## 五、“卸載DLL”按鈕的實現
這個功能的實現首先是獲取父窗口中所選進程的PID值,然后獲取當前列表框中所選擇的DLL的名稱,之后調用卸載函數即可:
~~~
void CDLLCheck::OnBtnUnInjectDll()
{
// TODO: Add your control notification handler code here
CProcessManageDlg *p;
p = (CProcessManageDlg *) GetParent();
int nPid = p->pid;
//獲取列表框中所選中的位置
POSITION Pos = m_CheckDLL.GetFirstSelectedItemPosition();
int nSelect = -1;
while ( Pos )
{
nSelect = m_CheckDLL.GetNextSelectedItem(Pos);
}
//如果在列表框中沒有進行選擇,則報錯
if ( -1 == nSelect )
{
AfxMessageBox("請選擇模塊!");
return;
}
//獲取列表框中DLL的名稱
char szDllName[MAX_PATH] = { 0 };
m_CheckDLL.GetItemText(nSelect, 1, szDllName, MAX_PATH);
UnInjectDll(nPid,szDllName);
ShowModule();
}
~~~
需要說明的是,上述程序中最后所使用的UnInjectDll(nPid,szDllName)函數,我曾經在[《反病毒攻防研究第010篇:DLL注入(中)——DLL注入與卸載器的編寫》](http://blog.csdn.net/ioio_jy/article/details/39404359)中討論過,這里不再論述。這個函數依舊要在新窗口的源程序以及頭文件中的相應位置進行聲明才能使用。
## 六、調整進程權限
一般來說,我們是無法查看系統進程的DLL文件的,主要是因為當前進程的權限級別不夠,除非當前進程擁有“SeDebugPrivilege”權限,獲取權限的步驟如下:
1、使用OpenProcessToken()函數打開當前進程的訪問令牌。
2、使用LookupPrivilegeValue()函數取得描述權限的LUID。
3、使用AdjustTokenPrivileges()函數調整訪問令牌的權限。
代碼如下:
~~~
void CDLLCheck::DebugPrivilege()
{
HANDLE hToken = NULL;
BOOL bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
if ( bRet == TRUE )
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
CloseHandle(hToken);
}
}
~~~
將代碼寫入新窗口的源程序,并填入窗口初始化的函數,使窗口生成時就擁有權限,最后在頭文件中聲明即可。
## 七、實際測試
為了測試本程序,可以參照[《DLL注入(中)——DLL注入與卸載器的編寫》](http://blog.csdn.net/ioio_jy/article/details/39404359)那樣先注入一個DLL,然后用本軟件進行查看并卸載:

圖4 查看并卸載DLL
經實際測試,程序可行,這又是我們反惡意程序的利器。
## 八、小結
通過兩篇文章的討論,完成了一個簡易的進程管理器。雖然簡單,但是很多時候它也能起到很大的功效。而通過這幾篇文章的討論,相信大家對于安全類軟件的編寫有了一定的認識,希望大家能夠不斷學習,將更加強大的功能添加到自己的軟件中,讓惡意程序無處藏身。