1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_sal.hxx" 26 27 #include <tchar.h> 28 29 #ifdef _MSC_VER 30 #pragma warning(push,1) // disable warnings within system headers 31 #endif 32 #define WIN32_LEAN_AND_MEAN 33 #include <windows.h> 34 #include <tlhelp32.h> 35 #include <psapi.h> 36 #ifdef _MSC_VER 37 #pragma warning(pop) 38 #endif 39 40 #include <signal.h> 41 #include <stdarg.h> 42 #include <stdlib.h> 43 #include <stdio.h> 44 45 #ifndef SIGNULL 46 #define SIGNULL 0 47 #endif 48 49 #ifndef SIGKILL 50 #define SIGKILL 9 51 #endif 52 53 #include <signal.h> 54 55 #define MAX_MODULES 1024 56 57 ///////////////////////////////////////////////////////////////////////////// 58 // Determines if a returned handle value is valid 59 ///////////////////////////////////////////////////////////////////////////// 60 61 static inline bool IsValidHandle( HANDLE handle ) 62 { 63 return INVALID_HANDLE_VALUE != handle && NULL != handle; 64 } 65 66 67 #define elementsof( a ) (sizeof(a) / sizeof( (a)[0] )) 68 69 ///////////////////////////////////////////////////////////////////////////// 70 // Retrieves function address in another process 71 ///////////////////////////////////////////////////////////////////////////// 72 73 #if 1 74 #define GetProcAddressEx( hProcess, hModule, lpProcName ) GetProcAddress( hModule, lpProcName ) 75 #else 76 FARPROC WINAPI GetProcAddressEx( HANDLE hProcess, HMODULE hModule, LPCSTR lpProcName ) 77 { 78 FARPROC lpfnProcAddress = GetProcAddress( hModule, lpProcName ); 79 80 if ( lpfnProcAddress ) 81 { 82 DWORD dwProcessId = GetProcessId( hProcess ); 83 84 if ( GetCurrentProcessId() != dwProcessId ) 85 { 86 FARPROC lpfnRemoteProcAddress = NULL; 87 TCHAR szBaseName[MAX_PATH]; 88 89 if ( GetModuleBaseName( GetCurrentProcess(), hModule, szBaseName, elementsof(szBaseName) ) ) 90 { 91 HMODULE ahModules[MAX_MODULES]; 92 DWORD cbNeeded = 0; 93 94 if ( EnumProcessModules( hProcess, ahModules, sizeof(ahModules), &cbNeeded ) ) 95 { 96 ULONG nModules = cbNeeded / sizeof(ahModules[0]); 97 98 for ( ULONG n = 0; n < nModules; n++ ) 99 { 100 TCHAR szRemoteBaseName[MAX_PATH]; 101 102 if ( GetModuleBaseName( 103 hProcess, ahModules[n], szRemoteBaseName, elementsof(szRemoteBaseName) ) && 104 0 == lstrcmpi( szRemoteBaseName, szBaseName ) 105 ) 106 { 107 lpfnRemoteProcAddress = lpfnProcAddress; 108 109 if ( ahModules[n] != hModule ) 110 *(LPBYTE*)&lpfnRemoteProcAddress += (LPBYTE)ahModules[n] - (LPBYTE)hModule; 111 break; 112 } 113 } 114 } 115 } 116 117 lpfnProcAddress = lpfnRemoteProcAddress; 118 } 119 } 120 121 return lpfnProcAddress; 122 } 123 #endif 124 125 ///////////////////////////////////////////////////////////////////////////// 126 // Raises a signal in an other process 127 ///////////////////////////////////////////////////////////////////////////// 128 129 static DWORD SignalToExceptionCode( int signal ) 130 { 131 switch ( signal ) 132 { 133 case SIGSEGV: 134 return EXCEPTION_ACCESS_VIOLATION; 135 case SIGFPE: 136 return EXCEPTION_FLT_INVALID_OPERATION; 137 case SIGILL: 138 return EXCEPTION_ILLEGAL_INSTRUCTION; 139 case SIGINT: 140 return CONTROL_C_EXIT; 141 case SIGBREAK: 142 return CONTROL_C_EXIT; 143 default: 144 return 0; 145 } 146 } 147 148 static BOOL RaiseSignalEx( HANDLE hProcess, int sig ) 149 { 150 DWORD dwProcessId = GetProcessId( hProcess ); 151 152 HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); 153 HANDLE hThread = 0; 154 BOOL fSuccess = FALSE; 155 156 if ( IsValidHandle(hSnapshot) ) 157 { 158 THREADENTRY32 te; 159 160 te.dwSize = sizeof(te); 161 fSuccess = Thread32First( hSnapshot, &te ); 162 while ( fSuccess ) 163 { 164 if ( te.th32OwnerProcessID == dwProcessId ) 165 { 166 hThread = OpenThread( 167 THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION | 168 THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, 169 FALSE, te.th32ThreadID ); 170 if ( IsValidHandle(hThread) ) 171 break; 172 } 173 174 fSuccess = Thread32Next( hSnapshot, &te ); 175 } 176 177 CloseHandle( hSnapshot ); 178 } 179 180 if ( fSuccess ) 181 { 182 CONTEXT aContext; 183 184 if ( SuspendThread( hThread ) != (DWORD)-1 ) 185 { 186 ZeroMemory( &aContext, sizeof(aContext) ); 187 aContext.ContextFlags = CONTEXT_FULL; 188 189 fSuccess = GetThreadContext( hThread, &aContext ); 190 191 if ( fSuccess ) 192 { 193 #if defined(INTEL) 194 if ( sig ) 195 { 196 DWORD dwStackBuffer[] = 197 { 198 aContext.Eip, 199 SignalToExceptionCode( sig ), 200 EXCEPTION_NONCONTINUABLE, 201 0, 202 0 203 }; 204 205 aContext.Esp -= sizeof(dwStackBuffer); 206 WriteProcessMemory( hProcess, (LPVOID)aContext.Esp, dwStackBuffer, sizeof(dwStackBuffer), NULL ); 207 aContext.Eip = (DWORD)GetProcAddressEx( hProcess, GetModuleHandleA("KERNEL32"), "RaiseException" ); 208 } 209 else 210 { 211 // FIXME: why? Does AMD64 need it too? 212 aContext.Ecx = aContext.Eax = aContext.Ebx = aContext.Edx = aContext.Esi = aContext.Edi = 0; 213 } 214 #elif defined(X86_64) 215 if ( sig ) 216 { 217 DWORD dwStackBuffer[] = 218 { 219 (DWORD)(aContext.Rip >> 32), 220 (DWORD)(aContext.Rip), 221 SignalToExceptionCode( sig ), 222 EXCEPTION_NONCONTINUABLE, 223 0, 224 0, 225 0 226 }; 227 228 aContext.Rsp -= sizeof(dwStackBuffer); 229 WriteProcessMemory( hProcess, (LPVOID)aContext.Rsp, dwStackBuffer, sizeof(dwStackBuffer), NULL ); 230 aContext.Rip = (DWORD64) GetProcAddressEx( hProcess, GetModuleHandleA("KERNEL32"), "RaiseException" ); 231 } 232 #endif 233 234 fSuccess = SetThreadContext( hThread, &aContext ); 235 } 236 237 fSuccess = ResumeThread( hThread ) && fSuccess; 238 239 DWORD dwLastError = GetLastError(); 240 CloseHandle( hThread ); 241 SetLastError( dwLastError ); 242 243 return fSuccess; 244 } 245 } 246 247 return FALSE; 248 } 249 ///////////////////////////////////////////////////////////////////////////// 250 // Command line parameter parsing 251 ///////////////////////////////////////////////////////////////////////////// 252 253 static void ParseCommandArgs( LPDWORD lpProcesses, LPDWORD lpdwNumProcesses, int *pSig ) 254 { 255 typedef struct _SignalEntry 256 { 257 LPCTSTR lpSignalName; 258 int iSignalValue; 259 } SignalEntry; 260 261 #define SIG_ENTRY( signal ) { TEXT(#signal), SIG##signal } 262 263 static SignalEntry SupportedSignals[] = 264 { 265 SIG_ENTRY( NULL ), 266 SIG_ENTRY( SEGV ), 267 SIG_ENTRY( ILL ), 268 SIG_ENTRY( FPE ), 269 SIG_ENTRY( INT ), 270 SIG_ENTRY( BREAK ), 271 SIG_ENTRY( TERM ), 272 SIG_ENTRY( ABRT ), 273 SIG_ENTRY( KILL ) 274 }; 275 276 const int NumSupportedSignals = elementsof(SupportedSignals); 277 278 DWORD dwMaxProcesses = *lpdwNumProcesses; 279 int argc = __argc; 280 TCHAR **argv = __targv; 281 282 *lpdwNumProcesses = 0; 283 284 for ( int argn = 1; argn < argc; argn++ ) 285 { 286 if ( 0 == lstrcmpi( argv[argn], TEXT("-l") ) || 287 0 == lstrcmpi( argv[argn], TEXT("/l") ) ) 288 289 { 290 for ( int n = 0; n < NumSupportedSignals; n++ ) 291 { 292 _tprintf( _T("%s "), SupportedSignals[n].lpSignalName ); 293 } 294 _tprintf( _T("\n") ); 295 ExitProcess( 0 ); 296 } 297 else if ( 0 == lstrcmpi( argv[argn], TEXT("-?") ) || 298 0 == lstrcmpi( argv[argn], TEXT("/?") ) || 299 0 == lstrcmpi( argv[argn], TEXT("-h") ) || 300 0 == lstrcmpi( argv[argn], TEXT("/h") ) || 301 0 == lstrcmpi( argv[argn], TEXT("--help") ) ) 302 { 303 _tprintf( 304 _T("Terminates a process by sending a signal.\n\n") 305 _T("Usage: kill [ -l ] [ -signal ] pid ...\n\n") 306 _T("-l Lists supported signals\n") 307 _T("-signal Sends the specified signal to the given processes.\n") 308 _T(" signal can be a numeric value specifying the signal number\n") 309 _T(" or a string listed by the -l parameter. If no signal is\n") 310 _T(" given SIGTERM (-TERM) is used.\n") 311 _T("pid Process id(s) or executables names(s) of processes to \n") 312 _T(" signal or terminate.\n\n") 313 ); 314 ExitProcess( 0 ); 315 } 316 else if ( argv[argn] && ( *argv[argn] == '-' || *argv[argn] == '/' ) ) 317 { 318 LPCTSTR argsig = CharNext( argv[argn] ); 319 320 int n; 321 for ( n = 0; n < NumSupportedSignals; n++ ) 322 { 323 _TCHAR *endptr = NULL; 324 325 if ( 0 == lstrcmpi( SupportedSignals[n].lpSignalName, argsig ) || 326 _tcstoul( argsig, &endptr, 0 ) == static_cast< unsigned >(SupportedSignals[n].iSignalValue) && (!endptr || !*endptr) ) 327 { 328 *pSig = SupportedSignals[n].iSignalValue; 329 break; 330 } 331 } 332 333 if ( n >= NumSupportedSignals ) 334 { 335 _ftprintf( stderr, 336 _T("kill: Illegal argument %s\n") 337 _T("Type 'kill --help' to show allowed syntax.\n") 338 _T("Type 'kill -l' to show supported signals.\n"), 339 argv[argn] ); 340 ExitProcess( 0 ); 341 } 342 } 343 else 344 { 345 unsigned long value = 0; 346 _TCHAR *endptr = NULL; 347 348 value = _tcstoul( argv[argn], &endptr, 0 ); 349 350 if ( !endptr || !*endptr ) 351 { 352 if ( *lpdwNumProcesses < dwMaxProcesses ) 353 { 354 *(lpProcesses++) = value; 355 (*lpdwNumProcesses)++; 356 } 357 } 358 else 359 { 360 HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); 361 362 if ( IsValidHandle( hSnapshot ) ) 363 { 364 PROCESSENTRY32 pe; 365 366 pe.dwSize = sizeof(pe); 367 BOOL fSuccess = Process32First( hSnapshot, &pe ); 368 369 while ( fSuccess ) 370 { 371 if ( 0 == lstrcmpi( argv[argn], pe.szExeFile ) ) 372 { 373 if ( *lpdwNumProcesses < dwMaxProcesses ) 374 { 375 *(lpProcesses++) = pe.th32ProcessID; 376 (*lpdwNumProcesses)++; 377 } 378 } 379 fSuccess = Process32Next( hSnapshot, &pe ); 380 } 381 382 CloseHandle( hSnapshot ); 383 } 384 } 385 } 386 } 387 388 if ( !*lpdwNumProcesses ) 389 { 390 _ftprintf( stderr, 391 _T("kill: No process specified.\n") 392 _T("Use kill --help to show allowed syntax.\n") 393 ); 394 ExitProcess( 0 ); 395 } 396 397 } 398 399 void OutputSystemMessage( DWORD dwErrorCode ) 400 { 401 LPVOID lpMsgBuf; 402 FormatMessageA( 403 FORMAT_MESSAGE_ALLOCATE_BUFFER | 404 FORMAT_MESSAGE_FROM_SYSTEM | 405 FORMAT_MESSAGE_IGNORE_INSERTS, 406 NULL, 407 dwErrorCode, 408 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language 409 (LPSTR)&lpMsgBuf, 410 0, 411 NULL 412 ); 413 414 printf( (LPSTR)lpMsgBuf ); 415 LocalFree( lpMsgBuf ); 416 } 417 418 int _tmain() 419 { 420 DWORD dwProcessIds[1024]; 421 DWORD nProcesses = elementsof(dwProcessIds); 422 int sig = SIGTERM; 423 424 425 ParseCommandArgs( dwProcessIds, &nProcesses, &sig ); 426 427 for ( ULONG n = 0; n < nProcesses; n++ ) 428 { 429 HANDLE hProcess; 430 431 _tprintf( _T("Sending signal to process id %d..."), dwProcessIds[n] ); 432 hProcess = OpenProcess( PROCESS_TERMINATE | PROCESS_CREATE_THREAD | SYNCHRONIZE | 433 PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, 434 FALSE, dwProcessIds[n] ); 435 436 if ( IsValidHandle( hProcess ) ) 437 { 438 if ( SIGKILL == sig ) 439 TerminateProcess( hProcess, 255 ); 440 else 441 { 442 if ( RaiseSignalEx( hProcess, sig ) ) 443 _tprintf( _T("OK\n") ); 444 else 445 { 446 OutputSystemMessage( GetLastError() ); 447 } 448 } 449 450 CloseHandle( hProcess ); 451 } 452 else 453 { 454 OutputSystemMessage( GetLastError() ); 455 } 456 } 457 458 return 0; 459 } 460 461