source: nscp/include/EnumProcess.cpp @ 739db5a

0.4.00.4.10.4.2
Last change on this file since 739db5a was 739db5a, checked in by Michael Medin <michael@…>, 5 years ago

First attempt at merging stable changes over to here (probably needs more work, but compiles and starts...)

  • Property mode set to 100644
File size: 11.1 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() : m_pProcesses(NULL), m_pModules(NULL), m_pCurrentP(NULL), m_pCurrentM(NULL), lpString(NULL), PSAPI(NULL)
34{
35        lpString = new TCHAR[MAX_FILENAME+1];
36
37        PSAPI = ::LoadLibrary(_TEXT("PSAPI"));
38        if (PSAPI) 
39        {
40                // Setup variables
41                m_MAX_COUNT = 256;
42                m_cProcesses = 0;
43                m_cModules   = 0;
44
45                // Find PSAPI functions
46                FEnumProcesses = (PFEnumProcesses)::GetProcAddress(PSAPI, "EnumProcesses");
47                FEnumProcessModules = (PFEnumProcessModules)::GetProcAddress(PSAPI, "EnumProcessModules");
48#ifdef UNICODE
49                FGetModuleFileNameEx = (PFGetModuleFileNameEx)::GetProcAddress(PSAPI, "GetModuleFileNameExW");
50#else
51                FGetModuleFileNameEx = (PFGetModuleFileNameEx)::GetProcAddress(PSAPI, "GetModuleFileNameExA");
52#endif
53        }
54
55        // Find the preferred method of enumeration
56        m_method = ENUM_METHOD::NONE;
57        int method = GetAvailableMethods();
58        if (method == (method|ENUM_METHOD::PSAPI))    m_method = ENUM_METHOD::PSAPI;
59
60}
61
62CEnumProcess::~CEnumProcess()
63{
64        delete [] lpString;
65        if (m_pProcesses) {delete[] m_pProcesses;}
66        if (m_pModules)   {delete[] m_pModules;}
67        if (PSAPI) FreeLibrary(PSAPI);
68}
69
70
71
72int CEnumProcess::GetAvailableMethods() {
73        int res = 0;
74        // Does all psapi functions exist?
75        if (PSAPI&&FEnumProcesses&&FEnumProcessModules&&FGetModuleFileNameEx)
76                res += ENUM_METHOD::PSAPI;
77        return res;
78}
79
80int CEnumProcess::SetMethod(int method) {
81        int avail = GetAvailableMethods();
82        if (avail == (method|avail))
83                m_method = method;
84        return m_method;
85}
86
87int CEnumProcess::GetSuggestedMethod()
88{
89        return m_method;
90}
91// Retrieves the first process in the enumeration. Should obviously be called before
92// GetProcessNext
93////////////////////////////////////////////////////////////////////////////////////
94BOOL CEnumProcess::GetProcessFirst(CEnumProcess::CProcessEntry *pEntry)
95{
96        if (ENUM_METHOD::NONE == m_method) {
97                return FALSE;
98        } else if ((ENUM_METHOD::PSAPI|m_method) == m_method) {
99                // Use PSAPI functions
100                // ----------------------
101                if (m_pProcesses) {delete[] m_pProcesses;}
102                m_pProcesses = new DWORD[m_MAX_COUNT];
103                m_pCurrentP = m_pProcesses;
104                DWORD cbNeeded = 0;
105                BOOL OK = FEnumProcesses(m_pProcesses, m_MAX_COUNT*sizeof(DWORD), &cbNeeded);
106
107                // We might need more memory here..
108                if (cbNeeded >= m_MAX_COUNT*sizeof(DWORD))
109                {
110                        m_MAX_COUNT += 256;
111                        return GetProcessFirst(pEntry); // Try again.
112                }
113
114                if (!OK) return FALSE;
115                m_cProcesses = cbNeeded/sizeof(DWORD);
116                return FillPStructPSAPI(*m_pProcesses, pEntry);
117        } else {
118                return FALSE;
119        }
120        return TRUE;
121}
122
123// Returns the following process
124////////////////////////////////////////////////////////////////
125BOOL CEnumProcess::GetProcessNext(CEnumProcess::CProcessEntry *pEntry)
126{
127        if (ENUM_METHOD::NONE == m_method) return FALSE;
128
129        // Use ToolHelp functions
130        // ----------------------
131        if ((ENUM_METHOD::PSAPI|m_method) == m_method) {
132                // Use PSAPI functions
133                // ----------------------
134                if (--m_cProcesses <= 0) return FALSE;
135                FillPStructPSAPI(*++m_pCurrentP, pEntry);
136        } else {
137                return FALSE;
138        }
139        return TRUE;
140}
141
142
143BOOL CEnumProcess::GetModuleFirst(DWORD dwPID, CEnumProcess::CModuleEntry *pEntry)
144{
145        if (ENUM_METHOD::NONE == m_method) return FALSE;
146        if ((ENUM_METHOD::PSAPI|m_method) == m_method) {
147                // Use PSAPI functions
148                // ----------------------
149                if (m_pModules) {delete[] m_pModules;}
150                m_pModules = new HMODULE[m_MAX_COUNT];
151                m_pCurrentM = m_pModules;
152                DWORD cbNeeded = 0;
153                HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
154                if (hProc)
155                {
156                        BOOL OK = FEnumProcessModules(hProc, m_pModules, m_MAX_COUNT*sizeof(HMODULE), &cbNeeded);
157                        CloseHandle(hProc);
158
159                        // We might need more memory here..
160                        if (cbNeeded >= m_MAX_COUNT*sizeof(HMODULE))
161                        {
162                                m_MAX_COUNT += 256;
163                                return GetModuleFirst(dwPID, pEntry); // Try again.
164                        }
165
166                        if (!OK) return FALSE;
167
168                        m_cModules = cbNeeded/sizeof(HMODULE);
169                        return FillMStructPSAPI(dwPID, *m_pCurrentM, pEntry);
170                }
171                return FALSE;
172        } else {
173                return FALSE;
174        }
175}
176
177
178BOOL CEnumProcess::GetModuleNext(DWORD dwPID, CEnumProcess::CModuleEntry *pEntry)
179{
180        if (ENUM_METHOD::NONE == m_method) return FALSE;
181        if ((ENUM_METHOD::PSAPI|m_method) == m_method) {
182                // Use PSAPI functions
183                // ----------------------
184                if (--m_cModules <= 0) return FALSE;
185                return FillMStructPSAPI(dwPID, *++m_pCurrentM, pEntry);
186        } else {
187                return FALSE;
188        }
189
190}
191
192
193BOOL CEnumProcess::EnableTokenPrivilege (LPTSTR privilege)
194{
195        HANDLE hToken;                       
196        TOKEN_PRIVILEGES token_privileges;                 
197        DWORD dwSize;                       
198        ZeroMemory (&token_privileges, sizeof (token_privileges));
199        token_privileges.PrivilegeCount = 1;
200        if ( !OpenProcessToken (GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
201                return FALSE;
202        if (!LookupPrivilegeValue ( NULL, privilege, &token_privileges.Privileges[0].Luid))
203        {
204                CloseHandle (hToken);
205                return FALSE;
206        }
207
208        token_privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
209        if (!AdjustTokenPrivileges ( hToken, FALSE, &token_privileges, 0, NULL, &dwSize))
210        {
211                CloseHandle (hToken);
212                return FALSE;
213        }
214        CloseHandle (hToken);
215        return TRUE;
216}
217
218// Process data block is found in an NT machine.
219// on an Intel system at 0x00020000  which is the 32
220// memory page. At offset 0x0498 is what I believe to be
221// the process' startup directory which is followed by
222// the system's PATH. Next is  process full command
223// followed by the exe name.
224#define PROCESS_DATA_BLOCK_ADDRESS      (LPVOID)0x00020498
225// align pointer
226#define ALIGNMENT(x) ( (x & 0xFFFFFFFC) ? (x & 0xFFFFFFFC) + sizeof(DWORD) : x )
227
228std::wstring CEnumProcess::GetCommandLine(HANDLE hProcess)
229{
230        SYSTEM_INFO sysinfo;
231        GetSystemInfo (&sysinfo);
232
233        MEMORY_BASIC_INFORMATION mbi;
234        if (VirtualQueryEx (hProcess, PROCESS_DATA_BLOCK_ADDRESS, &mbi, sizeof(mbi) ) == 0)
235                throw EnumProcException(_T("VirtualQueryEx failed"), GetLastError());
236        LPBYTE lpBuffer = (LPBYTE)malloc (sysinfo.dwPageSize);
237        if (lpBuffer == NULL)
238                throw EnumProcException(_T("Failed to allocate buffer"));
239        SIZE_T dwBytesRead;
240        if (!ReadProcessMemory( hProcess, mbi.BaseAddress, (LPVOID)lpBuffer, sysinfo.dwPageSize, &dwBytesRead)) {
241                free(lpBuffer);
242                throw EnumProcException(_T("ReadProcessMemory failed"), GetLastError());
243        }
244        LPBYTE lpPos = lpPos = lpBuffer + ((DWORD)PROCESS_DATA_BLOCK_ADDRESS - (DWORD)mbi.BaseAddress);
245
246        // Skip programs current directory and path
247        lpPos += (wcslen((LPWSTR)lpPos) + 1) * sizeof(WCHAR);
248
249        // Aligned on a DWORD boundary skip it, and copy the next string into
250        // buffer and null terminate it.
251        lpPos = (LPBYTE)ALIGNMENT((DWORD)lpPos);
252        lpPos += (wcslen((LPWSTR)lpPos) + 1) * sizeof(WCHAR);
253
254        // Sometimes there is an extra \0 here
255        /*
256        if ( *lpPos == '\0' )
257                lpPos += sizeof(WCHAR);
258        */
259
260        DWORD nStrLength = (wcslen((LPWSTR)lpPos) + 1) * sizeof(WCHAR);
261        WCHAR *buffer = new TCHAR[nStrLength+2];
262        buffer[0] = L'\0';
263        if(nStrLength > sizeof(WCHAR)) {
264                wcsncpy(buffer, (LPWSTR)lpPos, nStrLength);
265                buffer[nStrLength] = L'\0';
266        }
267        free(lpBuffer);
268        std::wstring ret = buffer;
269        delete [] buffer;
270        return ret;
271}
272
273
274BOOL CEnumProcess::FillPStructPSAPI(DWORD dwPID, CEnumProcess::CProcessEntry* pEntry)
275{
276        pEntry->dwPID = dwPID;
277        // Open process to get filename
278        bool bCmdLine = pEntry->getCommandLine();
279        DWORD openArgs = PROCESS_QUERY_INFORMATION|PROCESS_VM_READ;
280        if (bCmdLine)
281                openArgs |= PROCESS_VM_OPERATION;
282        HANDLE hProc = OpenProcess(openArgs, FALSE, dwPID);
283        if (!hProc) {
284                pEntry->filename = _T("N/A (security restriction)");
285                return TRUE;
286        }
287        if (bCmdLine) {
288                try {
289                        pEntry->command_line = GetCommandLine(hProc);
290                } catch (EnumProcException &e) {
291                        pEntry->command_line = _T("ERROR: " + e.getMessage(););
292                } catch (...) {
293                        pEntry->command_line = _T("ERROR: Failed to get CommandLine.");
294                }
295        }
296        HMODULE hMod;
297        DWORD size;
298        // Get the first module (the process itself)
299        if( FEnumProcessModules(hProc, &hMod, sizeof(hMod), &size) ) {
300                //Get filename
301                //GetModuleFileNameEx
302
303                if( !FGetModuleFileNameEx( hProc, hMod, lpString, MAX_FILENAME) ) {
304                        pEntry->filename = _T("N/A (error)");
305                } else {
306                        std::wstring path = lpString;
307                        std::wstring::size_type pos = path.find_last_of(_T("\\"));
308                        if (pos != std::wstring::npos) {
309                                path = path.substr(++pos);
310                        }
311                        pEntry->filename = path;
312                }
313        }
314        CloseHandle(hProc);
315        return TRUE;
316}
317
318
319BOOL CEnumProcess::FillMStructPSAPI(DWORD dwPID, HMODULE mMod, CEnumProcess::CModuleEntry *pEntry)
320{
321        HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
322        if (hProc)
323        {
324                if( !FGetModuleFileNameEx( hProc, mMod, lpString, MAX_FILENAME) )
325                {
326                        pEntry->sFilename = _T("N/A (error)");
327                } else {
328                        pEntry->sFilename = lpString;
329                }
330                pEntry->pLoadBase = (PVOID) mMod;
331                pEntry->pPreferredBase = GetModulePreferredBase(dwPID, (PVOID)mMod);
332                CloseHandle(hProc);
333                return TRUE;
334        }
335        return FALSE;
336}
337
338
339
340PVOID CEnumProcess::GetModulePreferredBase(DWORD dwPID, PVOID pModBase)
341{
342        if (ENUM_METHOD::NONE == m_method) return NULL;
343        HANDLE hProc = OpenProcess(PROCESS_VM_READ, FALSE, dwPID);
344        if (hProc)
345        {
346                IMAGE_DOS_HEADER idh;
347                IMAGE_NT_HEADERS inh;
348                //Read DOS header
349                ReadProcessMemory(hProc, pModBase, &idh, sizeof(idh), NULL);
350
351                if (IMAGE_DOS_SIGNATURE == idh.e_magic) // DOS header OK?
352                        // Read NT headers at offset e_lfanew
353                        ReadProcessMemory(hProc, (PBYTE)pModBase + idh.e_lfanew, &inh, sizeof(inh), NULL);
354
355                CloseHandle(hProc);
356
357                if (IMAGE_NT_SIGNATURE == inh.Signature) //NT signature OK?
358                        // Get the preferred base...
359                        return (PVOID) inh.OptionalHeader.ImageBase;
360
361        }
362
363        return NULL; //didn't find anything useful..
364}
365
366
Note: See TracBrowser for help on using the repository browser.