source: nscp/include/EnumProcess.cpp

Last change on this file was 94bb8cc, checked in by Michael Medin <michael@…>, 7 weeks ago

Fixed issue with commands escaping
Changed a lot of the utf16 into utf8 as well as major API refactoring

  • Property mode set to 100644
File size: 13.8 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 = pszFileName;
72        std::string::size_type pos = pEntry.command_line.find_last_of("\\");
73        if (pos != std::string::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("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("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("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("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("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("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        try {
126                enable_token_privilege(SE_DEBUG_NAME);
127        } catch (process_enumeration_exception &e) {
128                if (error_interface!=NULL)
129                        error_interface->report_warning(e.reason());
130        }
131
132        std::list<CProcessEntry> ret;
133        DWORD *dwPIDs = new DWORD[buffer_size+1];
134        DWORD cbNeeded = 0;
135        BOOL OK = FEnumProcesses(dwPIDs, buffer_size*sizeof(DWORD), &cbNeeded);
136        if (cbNeeded >= DEFAULT_BUFFER_SIZE*sizeof(DWORD)) {
137                delete [] dwPIDs;
138                if (error_interface!=NULL)
139                        error_interface->report_debug("Need larger buffer: " + strEx::s::xtos(buffer_size));
140                return enumerate_processes(expand_command_line, find_16bit, error_interface, buffer_size * 10);
141        }
142        if (!OK) {
143                delete [] dwPIDs;
144                throw process_enumeration_exception("Failed to enumerate process: " + error::lookup::last_error());
145        }
146        unsigned int process_count = cbNeeded/sizeof(DWORD);
147        for (unsigned int i = 0;i <process_count; ++i) {
148                if (dwPIDs[i] == 0)
149                        continue;
150                CProcessEntry entry;
151                entry.hung = false;
152                try {
153//                      if (error_interface!=NULL)
154//                              error_interface->report_debug_enter(_T("describe_pid"));
155                        try {
156                                entry = describe_pid(dwPIDs[i], expand_command_line);
157                        } catch (process_enumeration_exception &e) {
158                                if (error_interface!=NULL)
159                                        error_interface->report_debug(e.reason());
160                                if (expand_command_line) {
161                                        try {
162                                entry = describe_pid(dwPIDs[i], false);
163                                        } catch (process_enumeration_exception &e) {
164                                                if (error_interface!=NULL)
165                                                        error_interface->report_debug(e.reason());
166                                        }
167                                }
168                        }
169//                      if (error_interface!=NULL)
170//                              error_interface->report_debug_exit(_T("describe_pid"));
171                        if (VDMDBG!=NULL&&find_16bit) {
172                                if (error_interface!=NULL)
173                                        error_interface->report_debug("Looking for 16bit apps");
174                                if(stricmp(entry.filename.substr(0,9).c_str(), "NTVDM.EXE") == 0) {
175                                        find_16bit_container container;
176                                        container.target = &ret;
177                                        container.pid = entry.dwPID;
178                                        FVDMEnumTaskWOWEx(entry.dwPID, (TASKENUMPROCEX)&Enum16Proc, (LPARAM) &container);
179                                }
180                        }
181                        ret.push_back(entry);
182                } catch (process_enumeration_exception &e) {
183                        if (error_interface!=NULL)
184                                error_interface->report_error("Unhandeled exception describing PID: " + strEx::s::xtos(dwPIDs[i]) + ": " + e.reason());
185                } catch (...) {
186                        if (error_interface!=NULL)
187                                error_interface->report_error("Unknown exception describing PID: " + strEx::s::xtos(dwPIDs[i]));
188                }
189        }
190
191        std::vector<DWORD> hung_pids = find_crashed_pids(error_interface);
192        for (process_list::iterator entry = ret.begin(); entry != ret.end(); ++entry) {
193                if (std::find(hung_pids.begin(), hung_pids.end(), entry->dwPID) != hung_pids.end())
194                        (*entry).hung = true;
195                else
196                        (*entry).hung = false;
197        }
198
199        delete [] dwPIDs;
200        return ret;
201}
202
203struct enum_data {
204        CEnumProcess::error_reporter * error_interface;
205        std::vector<DWORD> crashed_pids;
206
207};
208
209BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam ) {
210        enum_data *data = reinterpret_cast<enum_data*>(lParam);
211        DWORD pid;
212        GetWindowThreadProcessId(hwnd, &pid);
213        if (GetWindow(hwnd, GW_OWNER) != NULL)
214                return TRUE;
215        PDWORD result;
216        if (!SendMessageTimeout(hwnd, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, 500, reinterpret_cast<PDWORD_PTR>(&result))) {
217                if (data->error_interface!=NULL)
218                        data->error_interface->report_debug("pid: " + strEx::s::xtos(pid) + " was hung");
219                data->crashed_pids.push_back(pid);
220        }
221
222//      TCHAR *buffer = new TCHAR[1024];
223//      int len = GetWindowText(hwnd, buffer, 1023);
224//      buffer[30] = 0;
225//      if (data->error_interface!=NULL)
226//              data->error_interface->report_debug(_T("pid: ") + res + strEx::itos(pid) + _T(" - ") + strEx::itos(len) + _T(" - ") + buffer);
227//      //std::wcout << _T("pid: ") << pid << _T(" - ") << len << _T(" : ") << buffer << std::endl;
228//      delete [] buffer;
229        return TRUE;
230}
231
232std::vector<DWORD> CEnumProcess::find_crashed_pids(CEnumProcess::error_reporter * error_interface) {
233        enum_data data;
234        data.error_interface = error_interface;
235        if(!EnumWindows(&EnumWindowsProc, reinterpret_cast<LPARAM>(&data))) {
236                if (error_interface)
237                        error_interface->report_error("Failed to enumerate windows: " + utf8::cvt<std::string>(error::lookup::last_error()));
238        }
239        return data.crashed_pids;
240}
241
242CEnumProcess::CProcessEntry CEnumProcess::describe_pid(DWORD pid, bool expand_command_line) {
243        CProcessEntry entry;
244        entry.dwPID = pid;
245        // Open process to get filename
246        DWORD openArgs = PROCESS_QUERY_INFORMATION|PROCESS_VM_READ;
247//      if (expand_command_line)
248//              openArgs |= PROCESS_VM_OPERATION;
249        HANDLE hProc = OpenProcess(openArgs, FALSE, pid);
250        if (!hProc)
251                throw process_enumeration_exception(GetLastError(), "Failed to open process: " + strEx::s::xtos(pid) + ": ");
252        if (expand_command_line)
253                entry.command_line = utf8::cvt<std::string>(GetCommandLine(hProc));
254        HMODULE hMod;
255        DWORD size;
256        // Get the first module (the process itself)
257        if( FEnumProcessModules(hProc, &hMod, sizeof(hMod), &size) ) {
258                TCHAR buffer[MAX_FILENAME+1];
259                if( !FGetModuleFileNameEx( hProc, hMod, reinterpret_cast<LPTSTR>(&buffer), MAX_FILENAME) ) {
260                        CloseHandle(hProc);
261                        throw process_enumeration_exception("Failed to find name for: " + strEx::s::xtos(pid) + ": " + error::lookup::last_error());
262                } else {
263                        std::wstring path = buffer;
264                        std::wstring::size_type pos = path.find_last_of(_T("\\"));
265                        if (pos != std::wstring::npos) {
266                                path = path.substr(++pos);
267                        }
268                        entry.filename = utf8::cvt<std::string>(path);
269                }
270        }
271
272        CloseHandle(hProc);
273        return entry;
274}
275
276typedef struct _PROCESS_BASIC_INFORMATION {
277        LONG ExitStatus;
278        LPVOID PebBaseAddress;
279        ULONG_PTR AffinityMask;
280        LONG BasePriority;
281        ULONG_PTR UniqueProcessId;
282        ULONG_PTR ParentProcessId;
283} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
284
285typedef struct _UNICODE_STRING {
286        USHORT Length;
287        USHORT MaximumLength;
288        PWSTR Buffer;
289} UNICODE_STRING, *PUNICODE_STRING;
290
291LPVOID GetPebAddress(HANDLE ProcessHandle) {
292        PFNtQueryInformationProcess NtQueryInformationProcess = (PFNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
293        if (NtQueryInformationProcess == NULL)
294                throw CEnumProcess::process_enumeration_exception("Failed to load NtQueryInformationProcess");
295        PROCESS_BASIC_INFORMATION pbi;
296        NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL);
297        return pbi.PebBaseAddress;
298}
299
300
301typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
302LPFN_ISWOW64PROCESS fnIsWow64Process;
303
304bool IsWow64(HANDLE hProcess, bool def = false) {
305        BOOL bIsWow64 = FALSE;
306        fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
307        if(NULL != fnIsWow64Process) {
308                if (!fnIsWow64Process(hProcess,&bIsWow64))
309                        return def;
310        }
311        return bIsWow64?true:false;
312}
313
314std::wstring CEnumProcess::GetCommandLine(HANDLE hProcess) {
315
316        UNICODE_STRING commandLine;
317#ifdef _WIN64
318        LPVOID pebAddress = GetPebAddress(hProcess);
319        LPVOID rtlUserProcParamsAddress;
320        if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x20, &rtlUserProcParamsAddress, sizeof(LPVOID), NULL))
321                throw process_enumeration_exception("Could not read the address of ProcessParameters: " + error::lookup::last_error());
322        if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x70, &commandLine, sizeof(commandLine), NULL))
323                throw process_enumeration_exception("Could not read commandline: " + error::lookup::last_error());
324#else
325        bool osIsWin64 = IsWow64(GetCurrentProcess());
326        if (!IsWow64(hProcess, !osIsWin64))
327                return _T("");
328        LPVOID pebAddress = GetPebAddress(hProcess);
329        LPVOID rtlUserProcParamsAddress;
330        if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10, &rtlUserProcParamsAddress, sizeof(LPVOID), NULL))
331                throw process_enumeration_exception("Could not read the address of ProcessParameters: " + error::lookup::last_error());
332        if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40, &commandLine, sizeof(commandLine), NULL))
333                throw process_enumeration_exception("Could not read commandline: " + error::lookup::last_error());
334#endif
335
336        /* allocate memory to hold the command line */
337        wchar_t *commandLineContents = new wchar_t[commandLine.Length+2];
338        memset(commandLineContents, 0, commandLine.Length);
339
340        /* read the command line */
341        if (!ReadProcessMemory(hProcess, commandLine.Buffer, commandLineContents, commandLine.Length, NULL)) {
342                delete [] commandLineContents;
343                throw process_enumeration_exception("Could not read commandline string: " + error::lookup::last_error());
344        }
345
346        commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0';
347        std::wstring ret = commandLineContents;
348        delete [] commandLineContents;
349
350        return ret;
351}
352
Note: See TracBrowser for help on using the repository browser.