1. 程式人生 > >安全類工具制作第005篇:進程管理器(下)

安全類工具制作第005篇:進程管理器(下)

btn creat lan 控件 lookup 包括 lln create tdi

一、前言

這次的程序是為了完好上一次所編寫的進程管理器。使得當我們選中某一個進程的時候。能夠查看其DLL文件,而且能夠對可疑的模塊進行卸載操作。這樣就能夠有效對抗DLL的惡意註入。

二、界面制作

這個界面是要依托於上一篇文章中制作的界面,須要單擊上次界面中的“查看DLL”button來啟動。

在上次的工作區中,找到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”button控件代碼

這裏所說的button。指的是上一個窗體中的“查看DLL”button控件。我希望在單擊這個button之後。可以彈出這次新建的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”button的實現

這個功能的實現首先是獲取父窗體中所選進程的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註入與卸載器的編寫》中討論過,這裏不再論述。這個函數依然要在新窗體的源程序以及頭文件裏的對應位置進行聲明才幹使用。

六、調整進程權限

一般來說,我們是無法查看系統進程的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註入與卸載器的編寫》那樣先註入一個DLL,然後用本軟件進行查看並卸載:
技術分享
圖4 查看並卸載DLL

經實際測試,程序可行,這又是我們反惡意程序的利器。

八、小結

通過兩篇文章的討論,完畢了一個簡易的進程管理器。盡管簡單,可是非常多時候它也能起到非常大的功效。而通過這幾篇文章的討論,相信大家對於安全類軟件的編寫有了一定的認識,希望大家可以不斷學習,將更加強大的功能加入到自己的軟件中,讓惡意程序無處藏身。


安全類工具制作第005篇:進程管理器(下)