source: nscp/include/EnumProcess.cpp @ 81a6048

0.4.00.4.10.4.2
Last change on this file since 81a6048 was 81a6048, checked in by Michael Medin <michael@…>, 17 months ago

Swapped APIs to make command line option for CheckProcess? work with x64...

  • Property mode set to 100644
File size: 14.0 KB
Line 
1/**************************************************************************
2*   Copyright (C) 2004-2007 by Michael Medin <michael@medin.name>         *
3*                                                                         *
4*   This code is part of NSClient++ - http://trac.nakednuns.org/nscp      *
5*                                                                         *
6*   This program is free software; you can redistribute it and/or modify  *
7*   it under the terms of the GNU General Public License as published by  *
8*   the Free Software Foundation; either version 2 of the License, or     *
9*   (at your option) any later version.                                   *
10*                                                                         *
11*   This program is distributed in the hope that it will be useful,       *
12*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14*   GNU General Public License for more details.                          *
15*                                                                         *
16*   You should have received a copy of the GNU General Public License     *
17*   along with this program; if not, write to the                         *
18*   Free Software Foundation, Inc.,                                       *
19*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
20***************************************************************************/
21#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
22#include <windows.h>
23#include <tchar.h>
24#include <iostream>
25
26#include "EnumProcess.h"
27
28//////////////////////////////////////////////////////////////////////
29// Construction/Destruction
30//////////////////////////////////////////////////////////////////////
31
32
33CEnumProcess::CEnumProcess() : PSAPI(NULL), VDMDBG(NULL), FVDMEnumTaskWOWEx(NULL)
34{
35        PSAPI = ::LoadLibrary(_TEXT("PSAPI"));
36        if (PSAPI) 
37        {
38                // Find PSAPI functions
39                FEnumProcesses = (PFEnumProcesses)::GetProcAddress(PSAPI, "EnumProcesses");
40                FEnumProcessModules = (PFEnumProcessModules)::GetProcAddress(PSAPI, "EnumProcessModules");
41#ifdef UNICODE
42                FGetModuleFileNameEx = (PFGetModuleFileNameEx)::GetProcAddress(PSAPI, "GetModuleFileNameExW");
43#else
44                FGetModuleFileNameEx = (PFGetModuleFileNameEx)::GetProcAddress(PSAPI, "GetModuleFileNameExA");
45#endif
46        }
47
48        VDMDBG = ::LoadLibrary(_TEXT("VDMDBG"));
49        if (VDMDBG)
50        {
51                // Find VDMdbg functions
52                FVDMEnumTaskWOWEx = (PFVDMEnumTaskWOWEx)::GetProcAddress(VDMDBG, "VDMEnumTaskWOWEx");
53        }
54}
55
56CEnumProcess::~CEnumProcess()
57{
58        if (PSAPI) FreeLibrary(PSAPI);
59        if (VDMDBG) FreeLibrary(VDMDBG);
60}
61
62struct find_16bit_container {
63        std::list<CEnumProcess::CProcessEntry> *target;
64        DWORD pid;
65};
66BOOL CALLBACK Enum16Proc( DWORD dwThreadId, WORD hMod16, WORD hTask16, PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined )
67{
68        find_16bit_container *container = reinterpret_cast<find_16bit_container*>(lpUserDefined);
69        CEnumProcess::CProcessEntry pEntry;
70        pEntry.dwPID = container->pid;
71        pEntry.command_line = strEx::string_to_wstring(pszFileName);
72        std::wstring::size_type pos = pEntry.command_line.find_last_of(_T("\\"));
73        if (pos != std::wstring::npos)
74                pEntry.filename = pEntry.command_line.substr(++pos);
75        else
76                pEntry.filename = pEntry.command_line;
77        container->target->push_back(pEntry);
78        return FALSE;
79}
80
81
82void CEnumProcess::enable_token_privilege(LPTSTR privilege)
83{
84        HANDLE hToken;                       
85        TOKEN_PRIVILEGES token_privileges;                 
86        DWORD dwSize;                       
87        ZeroMemory (&token_privileges, sizeof(token_privileges));
88        token_privileges.PrivilegeCount = 1;
89        if ( !OpenProcessToken (GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
90                throw process_enumeration_exception(_T("Failed to open process token: ") + error::lookup::last_error());
91        if (!LookupPrivilegeValue ( NULL, privilege, &token_privileges.Privileges[0].Luid)) {
92                CloseHandle (hToken);
93                throw process_enumeration_exception(_T("Failed to lookup privilege: ") + error::lookup::last_error());
94        }
95        token_privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
96        if (!AdjustTokenPrivileges ( hToken, FALSE, &token_privileges, 0, NULL, &dwSize)) {
97                CloseHandle (hToken);
98                throw process_enumeration_exception(_T("Failed to adjust token privilege: ") + error::lookup::last_error());
99        }
100        CloseHandle (hToken);
101}
102
103void CEnumProcess::disable_token_privilege(LPTSTR privilege)
104{
105        HANDLE hToken;                       
106        TOKEN_PRIVILEGES token_privileges;                 
107        DWORD dwSize;                       
108        ZeroMemory (&token_privileges, sizeof (token_privileges));
109        token_privileges.PrivilegeCount = 1;
110        if ( !OpenProcessToken (GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
111                throw process_enumeration_exception(_T("Failed to open process token: ") + error::lookup::last_error());
112        if (!LookupPrivilegeValue ( NULL, privilege, &token_privileges.Privileges[0].Luid)) {
113                CloseHandle (hToken);
114                throw process_enumeration_exception(_T("Failed to lookup privilege: ") + error::lookup::last_error());
115        }
116        token_privileges.Privileges[0].Attributes = SE_PRIVILEGE_REMOVED;
117        if (!AdjustTokenPrivileges ( hToken, FALSE, &token_privileges, 0, NULL, &dwSize)) {
118                CloseHandle (hToken);
119                throw process_enumeration_exception(_T("Failed to adjust token privilege: ") + error::lookup::last_error());
120        }
121        CloseHandle (hToken);
122}
123
124CEnumProcess::process_list CEnumProcess::enumerate_processes(bool expand_command_line, bool find_16bit, CEnumProcess::error_reporter *error_interface, unsigned int buffer_size) {
125        if (error_interface!=NULL)
126                error_interface->report_debug_enter(_T("enumerate_processes"));
127        try {
128                if (error_interface!=NULL)
129                        error_interface->report_debug_enter(_T("enable_token_privilege"));
130                enable_token_privilege(SE_DEBUG_NAME);
131                if (error_interface!=NULL)
132                        error_interface->report_debug_exit(_T("enable_token_privilege"));
133        } catch (process_enumeration_exception &e) {
134                if (error_interface!=NULL)
135                        error_interface->report_warning(e.what());
136        }
137
138        std::list<CProcessEntry> ret;
139        DWORD *dwPIDs = new DWORD[buffer_size+1];
140        DWORD cbNeeded = 0;
141        if (error_interface!=NULL)
142                error_interface->report_debug_enter(_T("FEnumProcesses"));
143        BOOL OK = FEnumProcesses(dwPIDs, buffer_size*sizeof(DWORD), &cbNeeded);
144        if (error_interface!=NULL)
145                error_interface->report_debug_exit(_T("FEnumProcesses"));
146        if (cbNeeded >= DEFAULT_BUFFER_SIZE*sizeof(DWORD)) {
147                delete [] dwPIDs;
148                if (error_interface!=NULL)
149                        error_interface->report_debug(_T("Need larger buffer: ") + strEx::itos(buffer_size));
150                return enumerate_processes(expand_command_line, find_16bit, error_interface, buffer_size * 10);
151        }
152        if (!OK) {
153                delete [] dwPIDs;
154                throw process_enumeration_exception(_T("Failed to enumerate process: ") + error::lookup::last_error());
155        }
156        unsigned int process_count = cbNeeded/sizeof(DWORD);
157        for (unsigned int i = 0;i <process_count; ++i) {
158                if (dwPIDs[i] == 0)
159                        continue;
160                CProcessEntry entry;
161                entry.hung = false;
162                try {
163//                      if (error_interface!=NULL)
164//                              error_interface->report_debug_enter(_T("describe_pid"));
165                        try {
166                                entry = describe_pid(dwPIDs[i], expand_command_line);
167                        } catch (process_enumeration_exception &e) {
168                                if (error_interface!=NULL)
169                                        error_interface->report_debug(e.what());
170                                if (expand_command_line) {
171                                        try {
172                                entry = describe_pid(dwPIDs[i], false);
173                                        } catch (process_enumeration_exception &e) {
174                                                if (error_interface!=NULL)
175                                                        error_interface->report_debug(e.what());
176                                        }
177                                }
178                        }
179//                      if (error_interface!=NULL)
180//                              error_interface->report_debug_exit(_T("describe_pid"));
181                        if (VDMDBG!=NULL&&find_16bit) {
182                                if (error_interface!=NULL)
183                                        error_interface->report_debug(_T("Looking for 16bit apps"));
184                                if( _wcsicmp(entry.filename.substr(0,9).c_str(), _T("NTVDM.EXE")) == 0) {
185                                        find_16bit_container container;
186                                        container.target = &ret;
187                                        container.pid = entry.dwPID;
188                                        FVDMEnumTaskWOWEx(entry.dwPID, (TASKENUMPROCEX)&Enum16Proc, (LPARAM) &container);
189                                }
190                        }
191                        ret.push_back(entry);
192                } catch (process_enumeration_exception &e) {
193                        if (error_interface!=NULL)
194                                error_interface->report_error(_T("Unhandeled exception describing PID: ") + strEx::itos(dwPIDs[i]) + _T(": ") + e.what());
195                } catch (...) {
196                        if (error_interface!=NULL)
197                                error_interface->report_error(_T("Unknown exception describing PID: ") + strEx::itos(dwPIDs[i]));
198                }
199        }
200
201        std::vector<DWORD> hung_pids = find_crashed_pids(error_interface);
202        for (process_list::iterator entry = ret.begin(); entry != ret.end(); ++entry) {
203                if (std::find(hung_pids.begin(), hung_pids.end(), entry->dwPID) != hung_pids.end())
204                        (*entry).hung = true;
205                else
206                        (*entry).hung = false;
207        }
208
209        delete [] dwPIDs;
210        if (error_interface!=NULL)
211                error_interface->report_debug_exit(_T("enumerate_processes"));
212        return ret;
213}
214
215struct enum_data {
216        CEnumProcess::error_reporter * error_interface;
217        std::vector<DWORD> crashed_pids;
218
219};
220
221BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam ) {
222        enum_data *data = reinterpret_cast<enum_data*>(lParam);
223        DWORD pid;
224        GetWindowThreadProcessId(hwnd, &pid);
225        if (GetWindow(hwnd, GW_OWNER) != NULL)
226                return TRUE;
227        PDWORD result;
228        if (!SendMessageTimeout(hwnd, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, 500, reinterpret_cast<PDWORD_PTR>(&result))) {
229                if (data->error_interface!=NULL)
230                        data->error_interface->report_debug(_T("pid: ") + strEx::itos(pid) + _T(" was hung"));
231                data->crashed_pids.push_back(pid);
232        }
233
234//      TCHAR *buffer = new TCHAR[1024];
235//      int len = GetWindowText(hwnd, buffer, 1023);
236//      buffer[30] = 0;
237//      if (data->error_interface!=NULL)
238//              data->error_interface->report_debug(_T("pid: ") + res + strEx::itos(pid) + _T(" - ") + strEx::itos(len) + _T(" - ") + buffer);
239//      //std::wcout << _T("pid: ") << pid << _T(" - ") << len << _T(" : ") << buffer << std::endl;
240//      delete [] buffer;
241        return TRUE;
242}
243
244std::vector<DWORD> CEnumProcess::find_crashed_pids(CEnumProcess::error_reporter * error_interface) {
245        if (error_interface)
246                error_interface->report_debug_enter(_T("find_crashed_pids"));
247        enum_data data;
248        data.error_interface = error_interface;
249        if(!EnumWindows(&EnumWindowsProc, reinterpret_cast<LPARAM>(&data))) {
250                if (error_interface)
251                        error_interface->report_error(_T("Failed to enumerate windows: ") + error::lookup::last_error());
252        }
253        if (error_interface)
254                error_interface->report_debug_exit(_T("find_crashed_pids"));
255        return data.crashed_pids;
256}
257
258CEnumProcess::CProcessEntry CEnumProcess::describe_pid(DWORD pid, bool expand_command_line) {
259        CProcessEntry entry;
260        entry.dwPID = pid;
261        // Open process to get filename
262        DWORD openArgs = PROCESS_QUERY_INFORMATION|PROCESS_VM_READ;
263//      if (expand_command_line)
264//              openArgs |= PROCESS_VM_OPERATION;
265        HANDLE hProc = OpenProcess(openArgs, FALSE, pid);
266        if (!hProc)
267                throw process_enumeration_exception(GetLastError(), _T("Failed to open process: ") + strEx::itos(pid) + _T(": "));
268        if (expand_command_line)
269                entry.command_line = GetCommandLine(hProc);
270        HMODULE hMod;
271        DWORD size;
272        // Get the first module (the process itself)
273        if( FEnumProcessModules(hProc, &hMod, sizeof(hMod), &size) ) {
274                TCHAR buffer[MAX_FILENAME+1];
275                if( !FGetModuleFileNameEx( hProc, hMod, reinterpret_cast<LPTSTR>(&buffer), MAX_FILENAME) ) {
276                        CloseHandle(hProc);
277                        throw process_enumeration_exception(_T("Failed to find name for: ") + strEx::itos(pid) + _T(": ") + error::lookup::last_error());
278                } else {
279                        std::wstring path = buffer;
280                        std::wstring::size_type pos = path.find_last_of(_T("\\"));
281                        if (pos != std::wstring::npos) {
282                                path = path.substr(++pos);
283                        }
284                        entry.filename = path;
285                }
286        }
287
288        CloseHandle(hProc);
289        return entry;
290}
291
292typedef struct _PROCESS_BASIC_INFORMATION {
293        LONG ExitStatus;
294        PVOID PebBaseAddress;
295        ULONG_PTR AffinityMask;
296        LONG BasePriority;
297        ULONG_PTR UniqueProcessId;
298        ULONG_PTR ParentProcessId;
299} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
300
301
302typedef struct _UNICODE_STRING {
303        USHORT Length;
304        USHORT MaximumLength;
305        PWSTR Buffer;
306} UNICODE_STRING, *PUNICODE_STRING;
307
308PVOID GetPebAddress(HANDLE ProcessHandle) {
309        PFNtQueryInformationProcess NtQueryInformationProcess = (PFNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
310        if (NtQueryInformationProcess == NULL)
311                throw CEnumProcess::process_enumeration_exception(_T("Failed to load NtQueryInformationProcess"));
312        PROCESS_BASIC_INFORMATION pbi;
313        NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL);
314        return pbi.PebBaseAddress;
315}
316
317std::wstring CEnumProcess::GetCommandLine(HANDLE hProcess) {
318
319        PVOID rtlUserProcParamsAddress;
320        UNICODE_STRING commandLine;
321        PVOID pebAddress = GetPebAddress(hProcess);
322
323
324#ifdef _WIN64
325        if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x20, &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
326                throw process_enumeration_exception(_T("Could not read the address of ProcessParameters: ") + error::lookup::last_error());
327#else
328        if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10, &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
329                throw process_enumeration_exception(_T("Could not read the address of ProcessParameters: ") + error::lookup::last_error());
330#endif
331
332#ifdef _WIN64
333        if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x70, &commandLine, sizeof(commandLine), NULL))
334                throw process_enumeration_exception(_T("Could not read commandline: ") + error::lookup::last_error());
335#else
336        if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40, &commandLine, sizeof(commandLine), NULL))
337                throw process_enumeration_exception(_T("Could not read commandline: ") + error::lookup::last_error());
338#endif
339
340        /* allocate memory to hold the command line */
341        wchar_t *commandLineContents = new wchar_t[commandLine.Length+2];
342        memset(commandLineContents, 0, commandLine.Length);
343
344        /* read the command line */
345        if (!ReadProcessMemory(hProcess, commandLine.Buffer, commandLineContents, commandLine.Length, NULL)) {
346                delete [] commandLineContents;
347                throw process_enumeration_exception(_T("Could not read commandline string: ") + error::lookup::last_error());
348        }
349
350        commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0';
351        std::wstring ret = commandLineContents;
352        delete [] commandLineContents;
353
354        return ret;
355}
356
Note: See TracBrowser for help on using the repository browser.