|
|
/*++
Module Name:
common.c
Abstract:
This module contains common apis used by tlist & kill.
--*/
#include <windows.h>
#include <winperf.h> // for Windows NT
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "inetdbgp.h"
//
// manifest constants
//
#define INITIAL_SIZE 51200
#define EXTEND_SIZE 25600
#define REGKEY_PERF _T("software\\microsoft\\windows nt\\currentversion\\perflib")
#define REGSUBKEY_COUNTERS _T("Counters")
#define PROCESS_COUNTER _T("process")
#define PROCESSID_COUNTER _T("id process")
#define TITLE_SIZE 64
typedef struct _SearchWin { LPCTSTR pExeName; LPDWORD pdwPid ; HWND* phwnd; } SearchWin ;
typedef struct _SearchMod { LPSTR pExeName; LPBOOL pfFound; } SearchMod ;
//
// prototypes
//
BOOL CALLBACK EnumWindowsProc2( HWND hwnd, LPARAM lParam );
HRESULT IsDllInProcess( DWORD dwProcessId, LPSTR pszName, LPBOOL pfFound );
//
// Functions
//
HRESULT KillTask( LPTSTR pName, LPSTR pszMandatoryModule ) /*++
Routine Description:
Provides an API for killing a task.
Arguments:
pName - process name to look for pszMandatoryModule - if non NULL then this module must be loaded in the process space for it to be killed.
Return Value:
Status
--*/ { DWORD rc;
TCHAR szSubKey[1024]; LANGID lid; HKEY hKeyNames = NULL;
DWORD dwType = 0; DWORD dwSize = 0; DWORD dwSpaceLeft = 0; LPBYTE buf = NULL; LPTSTR p; LPTSTR p2; PPERF_DATA_BLOCK pPerf; PPERF_OBJECT_TYPE pObj; PPERF_INSTANCE_DEFINITION pInst; PPERF_COUNTER_BLOCK pCounter; PPERF_COUNTER_DEFINITION pCounterDef; DWORD i; DWORD dwProcessIdTitle = 0; DWORD dwProcessIdCounter = 0; HRESULT hres = S_OK; HRESULT hresTemp = S_OK; HRESULT hresKill = S_OK;
//
// Look for the list of counters. Always use the neutral
// English version, regardless of the local language. We
// are looking for some particular keys, and we are always
// going to do our looking in English. We are not going
// to show the user the counter names, so there is no need
// to go find the corresponding name in the local language.
//
lid = MAKELANGID( LANG_ENGLISH, SUBLANG_NEUTRAL );
// There will be enough space in szSubKey to take the perf key
// along with the lid that has come in.
wsprintf( szSubKey, _T("%s\\%03x"), REGKEY_PERF, lid ); rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, szSubKey, 0, KEY_READ, &hKeyNames );
if (rc != ERROR_SUCCESS) { hres = HRESULT_FROM_WIN32( rc ); goto exit; }
//
// get the buffer size for the counter names
//
rc = RegQueryValueEx( hKeyNames, REGSUBKEY_COUNTERS, NULL, &dwType, NULL, &dwSize );
if (rc != ERROR_SUCCESS ) { hres = HRESULT_FROM_WIN32( rc ); goto exit; }
//
// allocate the counter names buffer
//
buf = (LPBYTE) malloc( dwSize ); if (buf == NULL) { hres = HRESULT_FROM_WIN32( GetLastError() ); goto exit; } memset( buf, 0, dwSize );
//
// read the counter names from the registry
//
rc = RegQueryValueEx( hKeyNames, REGSUBKEY_COUNTERS, NULL, &dwType, buf, &dwSize );
if (rc != ERROR_SUCCESS) { hres = HRESULT_FROM_WIN32( GetLastError() ); goto exit; }
//
// now loop thru the counter names looking for the following counters:
//
// 1. "Process" process name
// 2. "ID Process" process id
//
// the buffer contains multiple null terminated strings and then
// finally null terminated at the end. the strings are in pairs of
// counter number and counter name.
//
p = (LPTSTR)buf; while (*p) {
#pragma prefast(push)
#pragma prefast(disable:400, "Don't complain about case insensitive compares")
if (lstrcmpi(p, PROCESS_COUNTER) == 0) { //
// look backwards for the counter number
//
// buffer should of advanced far enough
// to have space before it now.
if ( ( LPVOID )p < ( LPVOID )(buf+2) ) { hres = E_FAIL; goto exit; }
// szSubkey is 1024 characters of space.
// we will be copying in a number some space
// and then the word "process", this should
// be plenty of space.
//
for( p2=p-2; _istdigit(*p2); p2--) { if ( ( LPVOID )p2 == ( LPVOID )buf ) { hres = E_FAIL; goto exit; } }
lstrcpy( szSubKey, p2+1 ); } else if (lstrcmpi(p, PROCESSID_COUNTER) == 0) {
// buffer should of advanced far enough
// to have space before it now.
if ( ( LPVOID )p < ( LPVOID )(buf+2) ) { hres = E_FAIL; goto exit; }
//
// look backwards for the counter number
//
for( p2=p-2; _istdigit(*p2); p2--) {
if ( ( LPVOID )p2 == ( LPVOID )buf ) { hres = E_FAIL; goto exit; } }
dwProcessIdTitle = _ttol( p2+1 ); } #pragma prefast(pop)
//
// next string
//
p += (lstrlen(p) + 1); }
//
// free the counter names buffer
//
free( buf );
//
// allocate the initial buffer for the performance data
//
dwSize = INITIAL_SIZE; buf = malloc( dwSize ); if (buf == NULL) { hres = HRESULT_FROM_WIN32( GetLastError() ); goto exit; } memset( buf, 0, dwSize );
for ( ; ; ) { rc = RegQueryValueEx( HKEY_PERFORMANCE_DATA, szSubKey, NULL, &dwType, buf, &dwSize );
pPerf = (PPERF_DATA_BLOCK) buf;
//
// check for success and valid perf data block signature
//
if ((rc == ERROR_SUCCESS) && (dwSize >= sizeof(PERF_DATA_BLOCK)) && (pPerf)->Signature[0] == (WCHAR)'P' && (pPerf)->Signature[1] == (WCHAR)'E' && (pPerf)->Signature[2] == (WCHAR)'R' && (pPerf)->Signature[3] == (WCHAR)'F' ) { break; }
//
// if buffer is not big enough, reallocate and try again
//
if (rc == ERROR_MORE_DATA) {
dwSize += EXTEND_SIZE;
free ( buf ); buf = NULL;
buf = malloc( dwSize ); if (buf == NULL) { hres = HRESULT_FROM_WIN32( GetLastError() ); goto exit; } memset( buf, 0, dwSize );
} else { // in the off case that we got data back under
// this key, but it was not the correct data, we
// need to return some error.
if ( rc == ERROR_SUCCESS ) { rc = ERROR_INVALID_DATA; } goto exit; } }
// make sure we don't ever walk past the end of the perf counter stuff.
// Subtract the space the PERF_DATA_BLOCK takes
dwSpaceLeft = dwSize - pPerf->HeaderLength;
// Validate that pObj will still be pointing
// to memory we just read.
if ( dwSpaceLeft < sizeof(PERF_OBJECT_TYPE) ) { rc = ERROR_INVALID_DATA; goto exit; } else { // Subtract the space the PERF_OBJECT_BLOCK takes
dwSpaceLeft = dwSpaceLeft - sizeof(PERF_OBJECT_TYPE); }
//
// set the perf_object_type pointer
//
pObj = (PPERF_OBJECT_TYPE) ((LPBYTE)pPerf + pPerf->HeaderLength);
//
// loop thru the performance counter definition records looking
// for the process id counter and then save its offset
//
// Validate that we have enough space for all the
// counter definitions we are expecting.
// to memory we just read.
if ( dwSpaceLeft < sizeof(PERF_COUNTER_DEFINITION) * pObj->NumCounters ) { rc = ERROR_INVALID_DATA; goto exit; } else { // Subtract the space the PERF_OBJECT_BLOCK takes
dwSpaceLeft = dwSpaceLeft - ( sizeof(PERF_COUNTER_DEFINITION) * pObj->NumCounters ) ; }
pCounterDef = (PPERF_COUNTER_DEFINITION) ((DWORD_PTR)pObj + pObj->HeaderLength); for (i=0; i<(DWORD)pObj->NumCounters; i++) { if (pCounterDef->CounterNameTitleIndex == dwProcessIdTitle) { dwProcessIdCounter = pCounterDef->CounterOffset; break; } pCounterDef++; }
pInst = (PPERF_INSTANCE_DEFINITION) ((LPBYTE)pObj + pObj->DefinitionLength);
//
// loop thru the performance instance data extracting each process name
// and process id
//
for (i=0; i<(DWORD)pObj->NumInstances; i++) { // Validate that we have enough space for the
// instance definition.
if ( dwSpaceLeft < sizeof(PERF_INSTANCE_DEFINITION) || dwSpaceLeft < pInst->ByteLength ) { rc = ERROR_INVALID_DATA; goto exit; } else { dwSpaceLeft = dwSpaceLeft - pInst->ByteLength; }
//
// pointer to the process name
//
p = (LPTSTR) ((LPBYTE)pInst + pInst->NameOffset);
//
// get the process id
//
pCounter = (PPERF_COUNTER_BLOCK) ((LPBYTE)pInst + pInst->ByteLength);
// Validate that we have enough space for the
// counter values we are expecting.
if ( dwSpaceLeft < sizeof(PERF_COUNTER_BLOCK) || dwSpaceLeft < pCounter->ByteLength ) { rc = ERROR_INVALID_DATA; goto exit; } else { dwSpaceLeft = dwSpaceLeft - pCounter->ByteLength; }
if ( lstrcmpi( p, pName ) == 0 ) { //
// Kill process now, do not update pTask array
//
BOOL fIsInProcess; DWORD dwProcessId = *((LPDWORD) ((LPBYTE)pCounter + dwProcessIdCounter));
if ( pszMandatoryModule == NULL || ( SUCCEEDED( hresTemp = IsDllInProcess( dwProcessId, pszMandatoryModule, &fIsInProcess ) ) && fIsInProcess ) ) { // OutputDebugStringW(L"Killing ");
// OutputDebugStringW(pName);
// OutputDebugStringW(L"\r\n");
hresTemp = KillProcess( dwProcessId ); }
// Need to remember the first failure, but we want
// to go on and try to kill the rest as well
if ( FAILED ( hresTemp ) && SUCCEEDED( hresKill ) ) { hresKill = hresTemp; }
}
//
// next process
//
pInst = (PPERF_INSTANCE_DEFINITION) ((LPBYTE)pCounter + pCounter->ByteLength); }
exit:
if (buf) { free( buf ); }
if ( hKeyNames != NULL ) { RegCloseKey( hKeyNames ); }
if ( SUCCEEDED ( hres ) ) { return hresKill; } else { return hres; } }
BOOL EnableDebugPrivNT( VOID )
/*++
Routine Description:
Changes the process's privilege so that kill works properly.
Arguments:
Return Value:
TRUE - success FALSE - failure
--*/
{ HANDLE hToken; LUID DebugValue; TOKEN_PRIVILEGES tkp;
//
// Retrieve a handle of the access token
//
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { return FALSE; }
//
// Enable the SE_DEBUG_NAME privilege
//
if (!LookupPrivilegeValue((LPTSTR) NULL, SE_DEBUG_NAME, &DebugValue)) { return FALSE; }
tkp.PrivilegeCount = 1; tkp.Privileges[0].Luid = DebugValue; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL);
//
// The return value of AdjustTokenPrivileges can't be tested
//
if (GetLastError() != ERROR_SUCCESS) { return FALSE; }
return TRUE; }
BOOLEAN EnumModulesCallback( LPVOID pParam, PMODULE_INFO pModuleInfo ) /*++
Routine Description:
Called by module enumerator with info on current module
Arguments:
pParam - as specified in the call to EnumModules() pModuleInfo - module information
Return Value:
TRUE to continue enumeration, FALSE to stop it
--*/ { if ( !_strcmpi( pModuleInfo->BaseName, ((SearchMod*)pParam)->pExeName ) ) { *((SearchMod*)pParam)->pfFound = TRUE;
return FALSE; // stop enumeration
}
return TRUE; }
HRESULT IsDllInProcess( DWORD dwProcessId, LPSTR pszName, LPBOOL pfFound ) /*++
Routine Description:
Check if a module ( e.g. DLL ) exists in specified process
Arguments:
dwProcessId - process ID to scan for module pszName pszName - module name to look for, e.g. "wam.dll" pfFound - updated with TRUE if pszName found in process dwProcessId valid only if functions succeed.
Return Value:
Status.
--*/ { HANDLE hProcess; HRESULT hres = S_OK; SearchMod sm;
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId ); if ( hProcess == NULL ) { // PID may have gone away while we were
// working on it, if it has then we will
// get invalid parameter when we try and
// open it.
if ( GetLastError() == ERROR_INVALID_PARAMETER ) { *pfFound = FALSE; return S_OK; }
return HRESULT_FROM_WIN32( GetLastError() ); }
sm.pExeName = pszName; sm.pfFound= pfFound; *pfFound = FALSE;
if ( !EnumModules( hProcess, EnumModulesCallback, (LPVOID)&sm ) ) { hres = E_FAIL; }
CloseHandle( hProcess );
return hres; }
HRESULT KillProcess( DWORD dwPid ) { HANDLE hProcess = NULL; HRESULT hres = S_OK;
hProcess = OpenProcess( PROCESS_TERMINATE, FALSE, dwPid );
if ( hProcess == NULL ) { // Process might have gone away since we found it.
if ( GetLastError() == ERROR_INVALID_PARAMETER ) { return S_OK; }
hres = HRESULT_FROM_WIN32( GetLastError() ); }
// OpenProcess worked
if ( SUCCEEDED(hres) ) { if (!TerminateProcess( hProcess, 1 )) { //
// If error code is access denied then the process may have
// all ready been terminated, so treat this as success. If
// it was not caused by the process all ready being terminated
// then we will catch the error below by timing out waiting
// for the process to disappear.
//
if ( GetLastError() == ERROR_ACCESS_DENIED ) { hres = S_OK; } else { hres = HRESULT_FROM_WIN32( GetLastError() ); } } else { hres = S_OK; }
CloseHandle( hProcess ); } return hres; }
VOID GetPidFromTitle( LPDWORD pdwPid, HWND* phwnd, LPCTSTR pExeName ) /*++
Routine Description:
Callback function for window enumeration.
Arguments:
pdwPid - updated with process ID of window matching window name or 0 if window not found phwnd - updated with window handle matching searched window name pExeName - window name to look for. Only the # of char present in this name will be used during checking for a match ( e.g. "inetinfo.exe" will match "inetinfo.exe - Application error"
Return Value:
None. *pdwPid will be 0 if no match is found
--*/ { SearchWin sw;
sw.pdwPid = pdwPid; sw.phwnd = phwnd; sw.pExeName = pExeName; *pdwPid = 0;
//
// enumerate all windows
//
EnumWindows( (WNDENUMPROC)EnumWindowsProc2, (LPARAM) &sw ); }
BOOL CALLBACK EnumWindowsProc2( HWND hwnd, LPARAM lParam ) /*++
Routine Description:
Callback function for window enumeration.
Arguments:
hwnd - window handle lParam - ptr to SearchWin
Return Value:
TRUE - continues the enumeration
--*/ { DWORD pid = 0; TCHAR buf[TITLE_SIZE]; SearchWin* psw = (SearchWin*)lParam;
//
// get the processid for this window
//
if (!GetWindowThreadProcessId( hwnd, &pid )) { return TRUE; }
if (GetWindowText( hwnd, buf, sizeof(buf)/sizeof(TCHAR) )) { if ( lstrlen( buf ) > lstrlen( psw->pExeName ) ) { buf[lstrlen( psw->pExeName )] = _T('\0'); }
if ( !lstrcmpi( psw->pExeName, buf ) ) { *psw->phwnd = hwnd; *psw->pdwPid = pid; } }
return TRUE; }
|