1.目标
需要实现一个pe文件的查看工具;

左边有两个listview;
上方是进程列表,下方是模块列表,进程列表中显示当前运行的所有进程信息;
点击进程列表中的某一个进程,在模块列表中显示该进程的所有模块信息;
点击右边的pe查看,先选一个pe文件,然后会弹出另一个窗口,显示该pe文件的信息;
2.需求分析
1)画界面
建一个win32窗口程序;
可以用资源脚本来建窗口,在工程中新建-》资源脚本-》拖控件;
有两个列表窗口,分别用来显示进程信息和pe文件信息;需要两个listview控件;
listview是win32通用控件,使用时需要加载comctl32.lib,并且要处理WM_NOTIFY消息;
2)进程遍历
要得到进程信息,需要用windows提供的api函数获取所有进程;
然后将进程信息写到listview中;
获取进程的步骤:
1】引入头文件tlhelp32.h;
2】获取进程快照
HWND pHandle = CreateToolhelp32Snapshot(0x2,0x0);
3】Process32Next(pHandle,&proc) 获取进程信息;
进程信息保存在PROCESSENTRY32结构中,是函数的第二个out参数;
函数的第一个参数为进程快照句柄;
当进程快照中没有下一个进程时,函数返回0;
PROCESSENTRY32 结构如下:
typedef struct tagPROCESSENTRY32 { DWORD dwSize; // 结构大小; DWORD cntUsage; // 此进程的引用计数; DWORD th32ProcessID; // 进程ID; DWORD th32DefaultHeapID; // 进程默认堆ID; DWORD th32ModuleID; // 进程模块ID; DWORD cntThreads; // 此进程开启的线程计数; DWORD th32ParentProcessID;// 父进程ID; LONG pcPriClassBase; // 线程优先权; DWORD dwFlags; // 保留; char szExeFile[MAX_PATH]; // 进程全名; } PROCESSENTRY32;
3)遍历进程的模块
点击一个进程,需要在下方的listview中显示该进程的所有模块信息;
因此需要遍历进程的模块;
需要用到windows提供的api函数;函数需要的参数为进程的pid;
为了知道pid,首先要知道是进程列表的那一行被点击;然后获取该行中表示pid的那一列;
判断那一行被点击需要处理WM_NOTIFY消息,通用控件都需要处理WM_NOTIFY;
也就是判断WM_NOTIFY消息的附加参数,条件为:进程的listview被点击,并且是右键点击时遍历模块;
遍历模块:
通过SendMessage发LVM_GETNEXTITEM消息,参数为LVNI_SELECTED,找到点击的是那一行;
然后SendMessage发LVM_GETITEMTEXT消息,传递行号和列号,找到该列的值即PID;
得到了进程PID就可以得到该进程模块信息,放到下方的listview即可;
通过进程pid获取该进程的快照,然后用 Module32Next遍历进程快照,模块信息保存在MODULEENTRY32结构中;
MODULEENTRY32结构:
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;
4)坑
因为没有权限,有一些进程无法得到模块快照;
3.实现代码
#include <windows.h> #include <stdio.h> #include <commctrl.h> #include <tlhelp32.h> #include "resource.h" #pragma comment(lib,"comctl32.lib") HINSTANCE hAppInstance; //提升进程权限 BOOL EnableDebugPrivilege() { HANDLE hToken; LUID Luid; TOKEN_PRIVILEGES tp; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))return FALSE; if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid)) { CloseHandle(hToken); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = Luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, false, &tp, sizeof(tp), NULL, NULL)) { CloseHandle(hToken); return FALSE; } CloseHandle(hToken); return TRUE; } //遍历进程 void enumProc(HWND hListProcess){ ListView_DeleteAllItems(hListProcess); HANDLE pHandle; PROCESSENTRY32 proc; DWORD procId; pHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0x0); if(pHandle==INVALID_HANDLE_VALUE){ return; } proc.dwSize = sizeof(PROCESSENTRY32); //给listview添加行 int i = 0; while(Process32Next(pHandle,&proc)){ LV_ITEM vitem; //初始化 memset(&vitem,0,sizeof(LV_ITEM)); vitem.mask = LVIF_TEXT; //获取进程名 vitem.pszText = proc.szExeFile; //值 vitem.iItem = i; //行 vitem.iSubItem = 0; //列 //ListView_InsertItem(hListProcess, &vitem); //一个宏和SendMessage作用一样 SendMessage(hListProcess, LVM_INSERTITEM,0,(DWORD)&vitem); //如果用SendMessage,给第一列赋值用LVM_INSERTITEM,其它列用LVM_SETITEM,用ListView同理 //获取进程ID TCHAR szPID[10] = {0}; sprintf(szPID,"%d",proc.th32ProcessID); //数字转字符串 vitem.pszText = szPID; vitem.iItem = i; vitem.iSubItem = 1; ListView_SetItem(hListProcess, &vitem); TCHAR szImageBase[0x20]; //进程基址 memset(szImageBase, 0, 0x20); TCHAR szImageSize[0x20]; //进程大小 memset(szImageSize, 0, 0x20); MODULEENTRY32 me32 = { 0 }; me32.dwSize = sizeof(MODULEENTRY32); HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, proc.th32ProcessID); if(hModuleSnap != INVALID_HANDLE_VALUE){ // 获取模块快照中第一条信息 if(Module32First(hModuleSnap, &me32)){ DWORD pProcessImageBase = (DWORD)me32.modBaseAddr; sprintf(szImageBase,"%08x",pProcessImageBase);; DWORD pProcessSize = (DWORD)me32.modBaseSize; sprintf(szImageSize,"%08x",pProcessSize); } } // 关闭句柄 ::CloseHandle(hModuleSnap); //获取像基址 vitem.pszText = szImageBase; vitem.iItem = i; vitem.iSubItem = 2; ListView_SetItem(hListProcess, &vitem); //获取镜像大小 vitem.pszText = szImageSize; vitem.iItem = i; vitem.iSubItem = 3; ListView_SetItem(hListProcess, &vitem); i++; } CloseHandle(pHandle); return; } //初始化进程窗口,就是给ListView控件添加列 void initProcessView(HWND hDlg){ LV_COLUMN lv; HWND hListProcess; //初始化,局部变量堆栈中分配,不知道是什么数据所以先清零 memset(&lv,0,sizeof(LV_COLUMN)); //获取listview控件句柄 hListProcess = GetDlgItem(hDlg,IDC_PROC); //设置整行选中,窗口是windows来管理的无法直接操作,程序能做的只能发送一个消息来让windows直到该怎么做 SendMessage(hListProcess,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT); //第一列 lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; lv.pszText = TEXT("进程"); //列标题 lv.cx = 150; //列宽 lv.iSubItem = 0; //ListView_InsertColumn(hListProcess, 0, &lv); SendMessage(hListProcess,LVM_INSERTCOLUMN,0,(DWORD)&lv); //第二列 lv.pszText = TEXT("PID"); lv.cx = 90; lv.iSubItem = 1; //ListView_InsertColumn(hListProcess, 1, &lv); SendMessage(hListProcess,LVM_INSERTCOLUMN,1,(DWORD)&lv); //第三列 lv.pszText = TEXT("镜像基址"); lv.cx = 100; lv.iSubItem = 2; ListView_InsertColumn(hListProcess, 2, &lv); //第四列 lv.pszText = TEXT("镜像大小"); lv.cx = 115; lv.iSubItem = 3; ListView_InsertColumn(hListProcess, 3, &lv); enumProc(hListProcess); } //遍历模块 void enumModule(HWND hListModule, HWND hListProcess, WPARAM wParam, LPARAM lParam){ DWORD rowId; LV_ITEM lv; TCHAR pid[0x20]; memset(&lv, 0, sizeof(lv)); memset(pid, 0, 0x20); //获取行 rowId = ::SendMessage(hListProcess, LVM_GETNEXTITEM, -1, LVNI_SELECTED); //获取选中的listview行号 if(rowId == -1){ MessageBox(NULL, TEXT("请选择进程"), TEXT("错误"), MB_OK); } //获取pid lv.iSubItem = 1; lv.pszText = pid; lv.cchTextMax = 0x20; ::SendMessage(hListProcess, LVM_GETITEMTEXT, rowId, (DWORD)&lv); //获取列的值 //MessageBox(NULL, pid, TEXT("进程id"), MB_OK); //根据pid获取模块信息并存储到下方的list行中 DWORD procId; sscanf( pid, "%d", &procId ); ListView_DeleteAllItems(hListModule); HANDLE hModuleSnap = INVALID_HANDLE_VALUE; MODULEENTRY32 me32; hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, procId); if(hModuleSnap != INVALID_HANDLE_VALUE){ int i = 0; while(Module32Next(hModuleSnap, &me32)){ //模块名 lv.pszText = me32.szModule; lv.iItem = i; lv.iSubItem = 0; ListView_InsertItem(hListModule, &lv); //模块路径 lv.pszText = me32.szExePath; lv.iItem = i; lv.iSubItem = 1; ListView_SetItem(hListModule, &lv); i++; } } return; } //初始化模块窗口 void initModuleView(HWND hDlg){ LV_COLUMN lv; HWND hListModule; //初始化,局部变量堆栈中分配,不知道是什么数据所以先清零 memset(&lv,0,sizeof(LV_COLUMN)); //获取listview控件句柄 hListModule = GetDlgItem(hDlg,IDC_DLL); //设置整行选中,窗口是windows来管理的无法直接操作,程序能做的只能发送一个消息来让windows直到该怎么做 SendMessage(hListModule,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT); //第一列 lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; lv.pszText = TEXT("模块名"); //列标题 lv.cx = 150; //列宽 lv.iSubItem = 0; //ListView_InsertColumn(hListProcess, 0, &lv); SendMessage(hListModule,LVM_INSERTCOLUMN,0,(DWORD)&lv); //第二列 lv.pszText = TEXT("模块位置"); lv.cx = 305; lv.iSubItem = 1; //ListView_InsertColumn(hListProcess, 1, &lv); SendMessage(hListModule,LVM_INSERTCOLUMN,1,(DWORD)&lv); } //消息处理回调函数 BOOL CALLBACK DialogProc( HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { HICON hicon_big; HICON hicon_small; switch(uMsg) { case WM_INITDIALOG : //加载图标 hicon_big = ::LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_ICON_BIG)); hicon_small = ::LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_ICON_SMALL)); SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (DWORD)hicon_big); SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (DWORD)hicon_small); //初始化列表 initProcessView(hwndDlg); initModuleView(hwndDlg); return TRUE; case WM_COMMAND : switch (LOWORD (wParam)) { case BTN_OUT: EndDialog(hwndDlg, 0); return TRUE; } break ; case WM_NOTIFY : { NMHDR* pNmhdr = (NMHDR*) lParam; if(wParam == IDC_PROC && pNmhdr->code == NM_CLICK){ enumModule(::GetDlgItem(hwndDlg, IDC_DLL), ::GetDlgItem(hwndDlg, IDC_PROC), wParam, lParam); //遍历模块,传递进程窗口句柄和两个消息附加参数 } } break; case WM_CLOSE: EndDialog(hwndDlg, 0); return TRUE; } return FALSE ; } //主窗口 int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ){ hAppInstance = hInstance; //加载通用控件 INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(INITCOMMONCONTROLSEX); icex.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&icex); //提权 EnableDebugPrivilege(); //画弹框 DialogBox( hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc ); return 0; }