网络安全编程:进程枚举

您所在的位置:网站首页 枚举进程模块 网络安全编程:进程枚举

网络安全编程:进程枚举

#网络安全编程:进程枚举| 来源: 网络整理| 查看: 265

原标题:网络安全编程:进程枚举

进程的枚举就是把所有的进程都列举一遍,列举的同时可以查找,可以显示等。当然,一些用特殊手段刻意隐藏的进程是无法通过常规的枚举方式枚举得到的。本文介绍应用层的进程枚举方法。编写的进程管理器的界面如图1所示。

图1 进程管理器

1. 进程及DLL枚举的API函数介绍

无论是枚举进程还是枚举进程中的DLL文件,方法都是相同的,都是通过创建指定的相关快照,再通过循环逐条获取快照的内容。类似的枚举线程、枚举堆都是相同的方法,差别只是在创建快照时参数不同,在逐条获得快照内容时的API函数不同而已。

枚举进程需要的API函数是CreateToolhelp32Snapshot、Process32First和Process32 Next。枚举线程时的API函数是CreateToolhelp32Snapshot、Thread32First和Thread 32Next。枚举进程中的DLL文件时的API函数是CreateToolhelp32Snapshot、Module32First和Module32Next。使用这些函数时,需要在代码中包含Tlhelp32.h头文件,否则在编译时会提示使用了未定义的函数。从上面这些API可以看出,无论是枚举进程、线程或者DLL,都需要调用CreateToolhelp32Snapshot这个函数,那么记住这个函数就比较重要了。当我们平时忘记具体的枚举函数时,只要记得CreateToolhelp32Snapshot函数,就可以通过这个函数在MSDN中找到其他枚举需要用的函数了。针对以上函数,下面分别进行介绍。

CreateToolhelp32Snapshot函数的定义如下:

HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID );

dwFlags:指明要建立系统快照的类型。对于要枚举的内容,该参数可以指定如下值。

TH32CS_SNAPMODULE:在枚举进程中的 DLL 时指定。

TH32CS_SNAPPROCESS:在枚举系统中的进程时指定。

TH32CS_SNAPTHREAD:在枚举系统中的线程时指定。

th32ProcessID:该参数根据 dwFlags 参数的不同而不同。如果枚举的是系统中的进程或系统中的线程,该参数为 NULL;如果枚举的是进程中加载的 DLL 的话,那么该参数是进程 ID。

该函数返回一个快照的句柄,并提供给枚举函数使用。

Process32First函数的定义如下:

BOOL WINAPI Process32First( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );

hSnapshot:该参数为 CreateToolhelp32Snapshot函数返回的句柄。

lppe:该参数为指向 PROCESSENTRY32 结构体的指针,该结构体的定义如下。

typedef struct tagPROCESSENTRY32 { DWORD dwSize; DWORD cntUsage; DWORD th32ProcessID; // 进程 ID ULONG_PTR th32DefaultHeapID; DWORD th32ModuleID; DWORD cntThreads; DWORD th32ParentProcessID; // 父进程 ID LONG pcPriClassBase; DWORD dwFlags; TCHAR szExeFile[MAX_PATH]; // 可执行文件的文件名 } PROCESSENTRY32; typedef PROCESSENTRY32 *PPROCESSENTRY32;

在使用该结构体时,需要对该结构体中的程序变量 dwSize 进行赋值。该变量保存PROCESSENTRY32结构体的大小。

Process32Next函数的定义如下:

BOOL WINAPI Process32Next( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );

该函数的使用方法与Process32First相同。

枚举进程中加载的DLL文件和枚举系统中的线程都与以上两个函数类似,所不同的是使用的XXX32First和XXX32Next的第2个参数指向的结构体不同。

对于枚举DLL文件来说,指向的结构体定义如下:

typedef struct tagMODULEENTRY32 { DWORD dwSize; DWORD th32ModuleID; DWORD th32ProcessID; DWORD GlblcntUsage; DWORD ProccntUsage; BYTE * modBaseAddr; DWORD modBaseSize; HMODULE hModule; TCHAR szModule[MAX_MODULE_NAME32 + 1]; TCHAR szExePath[MAX_PATH]; } MODULEENTRY32; typedef MODULEENTRY32 *PMODULEENTRY32;

对于枚举系统中的线程来说,指向的结构体定义如下:

typedef struct tagTHREADENTRY32{ DWORD dwSize; DWORD cntUsage; DWORD th32ThreadID; DWORD th32OwnerProcessID; LONG tpBasePri; LONG tpDeltaPri; DWORD dwFlags; } THREADENTRY32; typedef THREADENTRY32 *PTHREADENTRY32;

2. 枚举进程和枚举DLL的代码

下面用循环来枚举进程和枚举进程中加载的DLL。

枚举进程的代码如下:

VOID CManageProcessDlg::ShowProcess { // 清空列表框内容 m_ListProcess.DeleteAllItems; // 创建进程快照 HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if ( hSnap == INVALID_HANDLE_VALUE ) { AfxMessageBox("CreateToolhelp32Snapshot Error"); return ; } PROCESSENTRY32 Pe32 = { 0 }; Pe32.dwSize = sizeof(PROCESSENTRY32); BOOL bRet = Process32First(hSnap, &Pe32); int i = 0; CString str; // 循环获取进程快照中的每一项 while ( bRet ) { m_ListProcess.InsertItem(i, Pe32.szExeFile); str.Format("%d", Pe32.th32ProcessID); m_ListProcess.SetItemText(i, 1, str); i ++; bRet = Process32Next(hSnap, &Pe32); } CloseHandle(hSnap); }

枚举指定进程中加载的DLL的代码如下:

VOID CManageProcessDlg::ShowModule { // 清空列表框内容 m_ListModule.DeleteAllItems; // 获取选中的进程号 int nPid = GetSelectPid; // 进程 ID 为 0,则返回 if ( nPid == 0 ) { return ; } MODULEENTRY32 Me32 = { 0 }; Me32.dwSize = sizeof(MODULEENTRY32); // 创建模块快照 HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, nPid); if ( hSnap == INVALID_HANDLE_VALUE ) { AfxMessageBox("CreateToolhelp32Snapshot Error"); return ; } BOOL bRet = Module32First(hSnap, &Me32); int i = 0; CString str; // 循环获取模块快照中的每一项 while ( bRet ) { m_ListModule.InsertItem(i, Me32.szModule); m_ListModule.SetItemText(i, 1, Me32.szExePath); i ++; bRet = Module32Next(hSnap, &Me32); } CloseHandle(hSnap); }

在枚举DLL的代码中有两点需要说明。首先说明GetSelectPid函数,该函数用来获得选中的进程ID;其次,如果进程ID为0,则直接退出,不去获取其对应的DLL模块。这里给出GetSelectPid函数的实现,代码如下:

int CManageProcessDlg::GetSelectPid { int nPid = -1; POSITION Pos = m_ListProcess.GetFirstSelectedItemPosition; int nSelect = -1; while ( Pos ) { nSelect = m_ListProcess.GetNextSelectedItem(Pos); } if ( -1 == nSelect ) { AfxMessageBox("请选中要显示 DLL 的进程"); return -1; } char szPid[10] = { 0 }; m_ListProcess.GetItemText(nSelect, 1, szPid, 10); nPid = atoi(szPid); return nPid; }

该函数主要是MFC的控件的操作,这里给出其程序方便大家阅读。

3. 调整当前进程权限

编写的任务管理器已经完成了一部分,枚举系统进程和指定进程中的DLL的功能已经实现了。在VC6下编译连接并按Ctrl+F5键运行程序,可以看到任务管理器枚举出了系统中的进程。测试一下枚举进程中DLL的功能,选中“svchost.exe”进程,单击“查看DLL”按钮,“svchost.exe”进程中加载的DLL文件也都被枚举出来了。

程序看似是没有问题的,那么换一种方式让其运行。找到VC6生成好的任务管理器的可执行文件并双击运行它,再次选中“svchost.exe”进程,单击“查看DLL”按钮,是不是没有查看到“svchost.exe”进程加载的DLL文件?换一个其他的进程试试,比如选择自己编写的任务管理器测试是可以枚举到已经加载的DLL文件的。通过单击若干个进程可以发现,系统进程加载的DLL文件是无法枚举到的,但是在VC6下,通过Ctrl+F5组合键运行任务管理器是不存在该问题的。

无法枚举到系统进程加载的DLL列表的原因是CreateToolhelp32Snapshot函数调用失败,失败的原因是进程的权限不够。进程的权限不够,除了导致CreateToolhelp32Snapshot函数调用的失败,对于OpenProcess函数打开smss.exe、winlogon.exe等系统进程时同样也是会调用失败的。解决这个问题的方式是将进程的权限提升至“SeDebugPrivilege”。

调整进程权限的步骤如下:

① 使用OpenProcessToken函数打开当前进程的访问令牌。

② 使用LookupPrivilegeValue函数取得描述权限的LUID。

③ 使用AdjustTokenPrivileges函数调整访问令牌的权限。

调整权限使当前进程拥有“SeDebugPrivilege”权限。拥有该权限后,当前进程可以访问一些受限的系统资源。在远程线程注入的时候,同样需要调整当前进程的访问权限,否则是无法对系统进程进行远程线程注入的。因为在进行远程线程注入的时候,同样要用到OpenProcess函数。

调整当前进程权限的代码如下:

VOID CManageProcessDlg::DebugPrivilege { HANDLE hToken = NULL; BOOLbRet = 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); } }

参考文献:C++ 黑客编程揭秘与防范(第3版)返回搜狐,查看更多

责任编辑:



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3