一、前言
这次的程序是为了完好上一次所编写的进程管理器。使得当我们选中某一个进程的时候。能够查看其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”选项卡中进行例如以下设置:
单击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,然后用本软件进行查看并卸载:
经实际測试,程序可行,这又是我们反恶意程序的利器。
八、小结
通过两篇文章的讨论,完毕了一个简易的进程管理器。尽管简单,可是非常多时候它也能起到非常大的功效。而通过这几篇文章的讨论,相信大家对于安全类软件的编写有了一定的认识,希望大家可以不断学习,将更加强大的功能加入到自己的软件中,让恶意程序无处藏身。