1 /* 2 * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 #include <windows.h> 26 #include <string.h> 27 28 #include "jni.h" 29 #include "jni_util.h" 30 31 #include "sun_tools_attach_WindowsVirtualMachine.h" 32 33 34 /* kernel32 */ 35 typedef HINSTANCE (WINAPI* LoadLibraryFunc) (LPCTSTR); 36 typedef FARPROC (WINAPI* GetProcAddressFunc)(HMODULE, LPCSTR); 37 38 /* only on Windows 64-bit or 32-bit application running under WOW64 */ 39 typedef BOOL (WINAPI *IsWow64ProcessFunc) (HANDLE, PBOOL); 40 41 static LoadLibraryFunc _LoadLibrary; 42 static GetProcAddressFunc _GetProcAddress; 43 static IsWow64ProcessFunc _IsWow64Process; 44 45 /* psapi */ 46 typedef BOOL (WINAPI *EnumProcessModulesFunc) (HANDLE, HMODULE *, DWORD, LPDWORD ); 47 typedef DWORD (WINAPI *GetModuleFileNameExFunc) ( HANDLE, HMODULE, LPTSTR, DWORD ); 48 49 /* exported function in target VM */ 50 typedef jint (WINAPI* EnqueueOperationFunc) 51 (const char* cmd, const char* arg1, const char* arg2, const char* arg3, const char* pipename); 52 53 /* OpenProcess with SE_DEBUG_NAME privilege */ 54 static HANDLE 55 doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId); 56 57 /* convert jstring to C string */ 58 static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len); 59 60 61 /* 62 * Data copied to target process 63 */ 64 65 #define MAX_LIBNAME_LENGTH 16 66 #define MAX_FUNC_LENGTH 32 67 #define MAX_CMD_LENGTH 16 68 #define MAX_ARG_LENGTH 1024 69 #define MAX_ARGS 3 70 #define MAX_PIPE_NAME_LENGTH 256 71 72 typedef struct { 73 LoadLibraryFunc _LoadLibrary; 74 GetProcAddressFunc _GetProcAddress; 75 char jvmLib[MAX_LIBNAME_LENGTH]; /* "jvm.dll" */ 76 char func1[MAX_FUNC_LENGTH]; 77 char func2[MAX_FUNC_LENGTH]; 78 char cmd[MAX_CMD_LENGTH]; /* "load", "dump", ... */ 79 char arg[MAX_ARGS][MAX_ARG_LENGTH]; /* arguments to command */ 80 char pipename[MAX_PIPE_NAME_LENGTH]; 81 } DataBlock; 82 83 /* 84 * Return codes from enqueue function executed in target VM 85 */ 86 #define ERR_OPEN_JVM_FAIL 200 87 #define ERR_GET_ENQUEUE_FUNC_FAIL 201 88 89 90 /* 91 * Code copied to target process 92 */ 93 #pragma check_stack (off) 94 static DWORD WINAPI thread_func(DataBlock *pData) 95 { 96 HINSTANCE h; 97 EnqueueOperationFunc addr; 98 99 h = pData->_LoadLibrary(pData->jvmLib); 100 if (h == NULL) { 101 return ERR_OPEN_JVM_FAIL; 102 } 103 104 addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func1)); 105 if (addr == NULL) { 106 addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func2)); 107 } 108 if (addr == NULL) { 109 return ERR_GET_ENQUEUE_FUNC_FAIL; 110 } 111 112 /* "null" command - does nothing in the target VM */ 113 if (pData->cmd[0] == '\0') { 114 return 0; 115 } else { 116 return (*addr)(pData->cmd, pData->arg[0], pData->arg[1], pData->arg[2], pData->pipename); 117 } 118 } 119 120 /* This function marks the end of thread_func. */ 121 static void thread_end (void) { 122 } 123 #pragma check_stack 124 125 126 /* 127 * Class: sun_tools_attach_WindowsVirtualMachine 128 * Method: init 129 * Signature: ()V 130 */ 131 JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_init 132 (JNIEnv *env, jclass cls) 133 { 134 HINSTANCE h = LoadLibrary("kernel32"); 135 if (h != NULL) { 136 _LoadLibrary = (LoadLibraryFunc) GetProcAddress(h, "LoadLibraryA"); 137 _GetProcAddress = (GetProcAddressFunc)GetProcAddress(h, "GetProcAddress"); 138 _IsWow64Process = (IsWow64ProcessFunc)GetProcAddress(h, "IsWow64Process"); 139 } 140 if (_LoadLibrary == NULL || _GetProcAddress == NULL) { 141 JNU_ThrowInternalError(env, "Unable to get address of LoadLibraryA or GetProcAddress"); 142 } 143 } 144 145 146 /* 147 * Class: sun_tools_attach_WindowsVirtualMachine 148 * Method: generateStub 149 * Signature: ()[B 150 */ 151 JNIEXPORT jbyteArray JNICALL Java_sun_tools_attach_WindowsVirtualMachine_generateStub 152 (JNIEnv *env, jclass cls) 153 { 154 /* 155 * We should replace this with a real stub generator at some point 156 */ 157 DWORD len; 158 jbyteArray array; 159 160 len = (DWORD)((LPBYTE) thread_end - (LPBYTE) thread_func); 161 array= (*env)->NewByteArray(env, (jsize)len); 162 if (array != NULL) { 163 (*env)->SetByteArrayRegion(env, array, 0, (jint)len, (jbyte*)&thread_func); 164 } 165 return array; 166 } 167 168 /* 169 * Class: sun_tools_attach_WindowsVirtualMachine 170 * Method: openProcess 171 * Signature: (I)J 172 */ 173 JNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_openProcess 174 (JNIEnv *env, jclass cls, jint pid) 175 { 176 HANDLE hProcess = NULL; 177 178 if (pid == (jint) GetCurrentProcessId()) { 179 /* process is attaching to itself; get a pseudo handle instead */ 180 hProcess = GetCurrentProcess(); 181 /* duplicate the pseudo handle so it can be used in more contexts */ 182 if (DuplicateHandle(hProcess, hProcess, hProcess, &hProcess, 183 PROCESS_ALL_ACCESS, FALSE, 0) == 0) { 184 /* 185 * Could not duplicate the handle which isn't a good sign, 186 * but we'll try again with OpenProcess() below. 187 */ 188 hProcess = NULL; 189 } 190 } 191 192 if (hProcess == NULL) { 193 /* 194 * Attempt to open process. If it fails then we try to enable the 195 * SE_DEBUG_NAME privilege and retry. 196 */ 197 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); 198 if (hProcess == NULL && GetLastError() == ERROR_ACCESS_DENIED) { 199 hProcess = doPrivilegedOpenProcess(PROCESS_ALL_ACCESS, FALSE, 200 (DWORD)pid); 201 } 202 203 if (hProcess == NULL) { 204 if (GetLastError() == ERROR_INVALID_PARAMETER) { 205 JNU_ThrowIOException(env, "no such process"); 206 } else { 207 char err_mesg[255]; 208 /* include the last error in the default detail message */ 209 sprintf(err_mesg, "OpenProcess(pid=%d) failed; LastError=%d", 210 (int)pid, (int)GetLastError()); 211 JNU_ThrowIOExceptionWithLastError(env, err_mesg); 212 } 213 return (jlong)0; 214 } 215 } 216 217 /* 218 * On Windows 64-bit we need to handle 32-bit tools trying to attach to 64-bit 219 * processes (and visa versa). X-architecture attaching is currently not supported 220 * by this implementation. 221 */ 222 if (_IsWow64Process != NULL) { 223 BOOL isCurrent32bit, isTarget32bit; 224 (*_IsWow64Process)(GetCurrentProcess(), &isCurrent32bit); 225 (*_IsWow64Process)(hProcess, &isTarget32bit); 226 227 if (isCurrent32bit != isTarget32bit) { 228 CloseHandle(hProcess); 229 #ifdef _WIN64 230 JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException", 231 "Unable to attach to 32-bit process running under WOW64"); 232 #else 233 JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException", 234 "Unable to attach to 64-bit process"); 235 #endif 236 } 237 } 238 239 return (jlong)hProcess; 240 } 241 242 243 /* 244 * Class: sun_tools_attach_WindowsVirtualMachine 245 * Method: closeProcess 246 * Signature: (J)V 247 */ 248 JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_closeProcess 249 (JNIEnv *env, jclass cls, jlong hProcess) 250 { 251 CloseHandle((HANDLE)hProcess); 252 } 253 254 255 /* 256 * Class: sun_tools_attach_WindowsVirtualMachine 257 * Method: createPipe 258 * Signature: (Ljava/lang/String;)J 259 */ 260 JNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_createPipe 261 (JNIEnv *env, jclass cls, jstring pipename) 262 { 263 HANDLE hPipe; 264 char name[MAX_PIPE_NAME_LENGTH]; 265 266 jstring_to_cstring(env, pipename, name, MAX_PIPE_NAME_LENGTH); 267 268 hPipe = CreateNamedPipe( 269 name, // pipe name 270 PIPE_ACCESS_INBOUND, // read access 271 PIPE_TYPE_BYTE | // byte mode 272 PIPE_READMODE_BYTE | 273 PIPE_WAIT, // blocking mode 274 1, // max. instances 275 128, // output buffer size 276 8192, // input buffer size 277 NMPWAIT_USE_DEFAULT_WAIT, // client time-out 278 NULL); // default security attribute 279 280 if (hPipe == INVALID_HANDLE_VALUE) { 281 JNU_ThrowIOExceptionWithLastError(env, "CreateNamedPipe failed"); 282 } 283 return (jlong)hPipe; 284 } 285 286 /* 287 * Class: sun_tools_attach_WindowsVirtualMachine 288 * Method: closePipe 289 * Signature: (J)V 290 */ 291 JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_closePipe 292 (JNIEnv *env, jclass cls, jlong hPipe) 293 { 294 CloseHandle( (HANDLE)hPipe ); 295 } 296 297 /* 298 * Class: sun_tools_attach_WindowsVirtualMachine 299 * Method: connectPipe 300 * Signature: (J)V 301 */ 302 JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_connectPipe 303 (JNIEnv *env, jclass cls, jlong hPipe) 304 { 305 BOOL fConnected; 306 307 fConnected = ConnectNamedPipe((HANDLE)hPipe, NULL) ? 308 TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); 309 if (!fConnected) { 310 JNU_ThrowIOExceptionWithLastError(env, "ConnectNamedPipe failed"); 311 } 312 } 313 314 /* 315 * Class: sun_tools_attach_WindowsVirtualMachine 316 * Method: readPipe 317 * Signature: (J[BII)I 318 */ 319 JNIEXPORT jint JNICALL Java_sun_tools_attach_WindowsVirtualMachine_readPipe 320 (JNIEnv *env, jclass cls, jlong hPipe, jbyteArray ba, jint off, jint baLen) 321 { 322 unsigned char buf[128]; 323 DWORD len, nread, remaining; 324 BOOL fSuccess; 325 326 len = sizeof(buf); 327 remaining = (DWORD)(baLen - off); 328 if (len > remaining) { 329 len = remaining; 330 } 331 332 fSuccess = ReadFile( 333 (HANDLE)hPipe, // handle to pipe 334 buf, // buffer to receive data 335 len, // size of buffer 336 &nread, // number of bytes read 337 NULL); // not overlapped I/O 338 339 if (!fSuccess) { 340 if (GetLastError() == ERROR_BROKEN_PIPE) { 341 return (jint)-1; 342 } else { 343 JNU_ThrowIOExceptionWithLastError(env, "ReadFile"); 344 } 345 } else { 346 if (nread == 0) { 347 return (jint)-1; // EOF 348 } else { 349 (*env)->SetByteArrayRegion(env, ba, off, (jint)nread, (jbyte *)(buf+off)); 350 } 351 } 352 353 return (jint)nread; 354 } 355 356 357 /* 358 * Class: sun_tools_attach_WindowsVirtualMachine 359 * Method: enqueue 360 * Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V 361 */ 362 JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_enqueue 363 (JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd, 364 jstring pipename, jobjectArray args) 365 { 366 DataBlock data; 367 DataBlock* pData; 368 DWORD* pCode; 369 DWORD stubLen; 370 HANDLE hProcess, hThread; 371 jint argsLen, i; 372 jbyte* stubCode; 373 jboolean isCopy; 374 375 /* 376 * Setup data to copy to target process 377 */ 378 data._LoadLibrary = _LoadLibrary; 379 data._GetProcAddress = _GetProcAddress; 380 381 strcpy(data.jvmLib, "jvm"); 382 strcpy(data.func1, "JVM_EnqueueOperation"); 383 strcpy(data.func2, "_JVM_EnqueueOperation@20"); 384 385 /* 386 * Command and arguments 387 */ 388 jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH); 389 argsLen = (*env)->GetArrayLength(env, args); 390 391 if (argsLen > 0) { 392 if (argsLen > MAX_ARGS) { 393 JNU_ThrowInternalError(env, "Too many arguments"); 394 } 395 for (i=0; i<argsLen; i++) { 396 jobject obj = (*env)->GetObjectArrayElement(env, args, i); 397 if (obj == NULL) { 398 data.arg[i][0] = '\0'; 399 } else { 400 jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH); 401 } 402 if ((*env)->ExceptionOccurred(env)) return; 403 } 404 } 405 for (i=argsLen; i<MAX_ARGS; i++) { 406 data.arg[i][0] = '\0'; 407 } 408 409 /* pipe name */ 410 jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH); 411 412 /* 413 * Allocate memory in target process for data and code stub 414 * (assumed aligned and matches architecture of target process) 415 */ 416 hProcess = (HANDLE)handle; 417 418 pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE ); 419 if (pData == NULL) { 420 JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed"); 421 return; 422 } 423 WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL ); 424 425 426 stubLen = (DWORD)(*env)->GetArrayLength(env, stub); 427 stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy); 428 429 pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); 430 if (pCode == NULL) { 431 JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed"); 432 VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); 433 return; 434 } 435 WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL ); 436 if (isCopy) { 437 (*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT); 438 } 439 440 /* 441 * Create thread in target process to execute code 442 */ 443 hThread = CreateRemoteThread( hProcess, 444 NULL, 445 0, 446 (LPTHREAD_START_ROUTINE) pCode, 447 pData, 448 0, 449 NULL ); 450 if (hThread != NULL) { 451 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) { 452 JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed"); 453 } else { 454 DWORD exitCode; 455 GetExitCodeThread(hThread, &exitCode); 456 if (exitCode) { 457 switch (exitCode) { 458 case ERR_OPEN_JVM_FAIL : 459 JNU_ThrowIOException(env, 460 "jvm.dll not loaded by target process"); 461 break; 462 case ERR_GET_ENQUEUE_FUNC_FAIL : 463 JNU_ThrowIOException(env, 464 "Unable to enqueue operation: the target VM does not support attach mechanism"); 465 break; 466 default : 467 JNU_ThrowInternalError(env, 468 "Remote thread failed for unknown reason"); 469 } 470 } 471 } 472 CloseHandle(hThread); 473 } else { 474 JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed"); 475 } 476 477 VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE); 478 VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); 479 } 480 481 /* 482 * Attempts to enable the SE_DEBUG_NAME privilege and open the given process. 483 */ 484 static HANDLE 485 doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) { 486 HANDLE hToken; 487 HANDLE hProcess = NULL; 488 LUID luid; 489 TOKEN_PRIVILEGES tp, tpPrevious; 490 DWORD retLength, error; 491 492 /* 493 * Get the access token 494 */ 495 if (!OpenThreadToken(GetCurrentThread(), 496 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, 497 FALSE, 498 &hToken)) { 499 if (GetLastError() != ERROR_NO_TOKEN) { 500 return (HANDLE)NULL; 501 } 502 503 /* 504 * No access token for the thread so impersonate the security context 505 * of the process. 506 */ 507 if (!ImpersonateSelf(SecurityImpersonation)) { 508 return (HANDLE)NULL; 509 } 510 if (!OpenThreadToken(GetCurrentThread(), 511 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, 512 FALSE, 513 &hToken)) { 514 return (HANDLE)NULL; 515 } 516 } 517 518 /* 519 * Get LUID for the privilege 520 */ 521 if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) { 522 error = GetLastError(); 523 CloseHandle(hToken); 524 SetLastError(error); 525 return (HANDLE)NULL; 526 } 527 528 /* 529 * Enable the privilege 530 */ 531 ZeroMemory(&tp, sizeof(tp)); 532 tp.PrivilegeCount = 1; 533 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 534 tp.Privileges[0].Luid = luid; 535 536 error = 0; 537 if (AdjustTokenPrivileges(hToken, 538 FALSE, 539 &tp, 540 sizeof(TOKEN_PRIVILEGES), 541 &tpPrevious, 542 &retLength)) { 543 /* 544 * If we enabled the privilege then attempt to open the 545 * process. 546 */ 547 if (GetLastError() == ERROR_SUCCESS) { 548 hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); 549 if (hProcess == NULL) { 550 error = GetLastError(); 551 } 552 } else { 553 error = ERROR_ACCESS_DENIED; 554 } 555 556 /* 557 * Revert to the previous privileges 558 */ 559 AdjustTokenPrivileges(hToken, 560 FALSE, 561 &tpPrevious, 562 retLength, 563 NULL, 564 NULL); 565 } else { 566 error = GetLastError(); 567 } 568 569 570 /* 571 * Close token and restore error 572 */ 573 CloseHandle(hToken); 574 SetLastError(error); 575 576 return hProcess; 577 } 578 579 /* convert jstring to C string */ 580 static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len) { 581 jboolean isCopy; 582 const char* str; 583 584 if (jstr == NULL) { 585 cstr[0] = '\0'; 586 } else { 587 str = JNU_GetStringPlatformChars(env, jstr, &isCopy); 588 strncpy(cstr, str, len); 589 cstr[len-1] = '\0'; 590 if (isCopy) { 591 JNU_ReleaseStringPlatformChars(env, jstr, str); 592 } 593 } 594 }