1 /* 2 * Copyright (c) 2005, 2014, 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 <Sddl.h> 27 #include <string.h> 28 29 #include "jni.h" 30 #include "jni_util.h" 31 32 #include "sun_tools_attach_VirtualMachineImpl.h" 33 34 35 /* kernel32 */ 36 typedef HINSTANCE (WINAPI* GetModuleHandleFunc) (LPCTSTR); 37 typedef FARPROC (WINAPI* GetProcAddressFunc)(HMODULE, LPCSTR); 38 39 /* only on Windows 64-bit or 32-bit application running under WOW64 */ 40 typedef BOOL (WINAPI *IsWow64ProcessFunc) (HANDLE, PBOOL); 41 42 static GetModuleHandleFunc _GetModuleHandle; 43 static GetProcAddressFunc _GetProcAddress; 44 static IsWow64ProcessFunc _IsWow64Process; 45 46 /* psapi */ 47 typedef BOOL (WINAPI *EnumProcessModulesFunc) (HANDLE, HMODULE *, DWORD, LPDWORD ); 48 typedef DWORD (WINAPI *GetModuleFileNameExFunc) ( HANDLE, HMODULE, LPTSTR, DWORD ); 49 50 /* exported function in target VM */ 51 typedef jint (WINAPI* EnqueueOperationFunc) 52 (const char* cmd, const char* arg1, const char* arg2, const char* arg3, const char* pipename); 53 54 /* OpenProcess with SE_DEBUG_NAME privilege */ 55 static HANDLE 56 doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId); 57 58 /* convert jstring to C string */ 59 static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len); 60 61 62 /* 63 * Data copied to target process 64 */ 65 66 #define MAX_LIBNAME_LENGTH 16 67 #define MAX_FUNC_LENGTH 32 68 #define MAX_CMD_LENGTH 16 69 #define MAX_ARG_LENGTH 1024 70 #define MAX_ARGS 3 71 #define MAX_PIPE_NAME_LENGTH 256 72 73 typedef struct { 74 GetModuleHandleFunc _GetModuleHandle; 75 GetProcAddressFunc _GetProcAddress; 76 char jvmLib[MAX_LIBNAME_LENGTH]; /* "jvm.dll" */ 77 char func1[MAX_FUNC_LENGTH]; 78 char func2[MAX_FUNC_LENGTH]; 79 char cmd[MAX_CMD_LENGTH]; /* "load", "dump", ... */ 80 char arg[MAX_ARGS][MAX_ARG_LENGTH]; /* arguments to command */ 81 char pipename[MAX_PIPE_NAME_LENGTH]; 82 } DataBlock; 83 84 /* 85 * Return codes from enqueue function executed in target VM 86 */ 87 #define ERR_OPEN_JVM_FAIL 200 88 #define ERR_GET_ENQUEUE_FUNC_FAIL 201 89 90 91 /* 92 * Code copied to target process 93 */ 94 #pragma check_stack (off) 95 /* Switch off all runtime checks (checks caused by /RTC<x>). They cause the 96 * generated code to contain relative jumps to check functions which make 97 * the code position dependent. */ 98 #pragma runtime_checks ("scu", off) 99 DWORD WINAPI jvm_attach_thread_func(DataBlock *pData) 100 { 101 HINSTANCE h; 102 EnqueueOperationFunc addr; 103 104 h = pData->_GetModuleHandle(pData->jvmLib); 105 if (h == NULL) { 106 return ERR_OPEN_JVM_FAIL; 107 } 108 109 addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func1)); 110 if (addr == NULL) { 111 addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func2)); 112 } 113 if (addr == NULL) { 114 return ERR_GET_ENQUEUE_FUNC_FAIL; 115 } 116 117 /* "null" command - does nothing in the target VM */ 118 if (pData->cmd[0] == '\0') { 119 return 0; 120 } else { 121 return (*addr)(pData->cmd, pData->arg[0], pData->arg[1], pData->arg[2], pData->pipename); 122 } 123 } 124 125 /* This function marks the end of jvm_attach_thread_func. */ 126 void jvm_attach_thread_func_end (void) { 127 } 128 #pragma check_stack 129 #pragma runtime_checks ("scu", restore) 130 131 /* 132 * Class: sun_tools_attach_VirtualMachineImpl 133 * Method: init 134 * Signature: ()V 135 */ 136 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_init 137 (JNIEnv *env, jclass cls) 138 { 139 // All following APIs exist on Windows XP with SP2/Windows Server 2008 140 _GetModuleHandle = (GetModuleHandleFunc)GetModuleHandle; 141 _GetProcAddress = (GetProcAddressFunc)GetProcAddress; 142 _IsWow64Process = (IsWow64ProcessFunc)IsWow64Process; 143 } 144 145 146 /* 147 * Class: sun_tools_attach_VirtualMachineImpl 148 * Method: generateStub 149 * Signature: ()[B 150 */ 151 JNIEXPORT jbyteArray JNICALL Java_sun_tools_attach_VirtualMachineImpl_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) jvm_attach_thread_func_end - (LPBYTE) jvm_attach_thread_func); 161 array= (*env)->NewByteArray(env, (jsize)len); 162 if (array != NULL) { 163 (*env)->SetByteArrayRegion(env, array, 0, (jint)len, (jbyte*)&jvm_attach_thread_func); 164 } 165 return array; 166 } 167 168 /* 169 * Class: sun_tools_attach_VirtualMachineImpl 170 * Method: openProcess 171 * Signature: (I)J 172 */ 173 JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_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=0x%x", 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_VirtualMachineImpl 245 * Method: closeProcess 246 * Signature: (J)V 247 */ 248 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_closeProcess 249 (JNIEnv *env, jclass cls, jlong hProcess) 250 { 251 CloseHandle((HANDLE)hProcess); 252 } 253 254 255 /* 256 * Class: sun_tools_attach_VirtualMachineImpl 257 * Method: createPipe 258 * Signature: (Ljava/lang/String;)J 259 */ 260 JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_createPipe 261 (JNIEnv *env, jclass cls, jstring pipename) 262 { 263 HANDLE hPipe; 264 char name[MAX_PIPE_NAME_LENGTH]; 265 266 SECURITY_ATTRIBUTES sa; 267 LPSECURITY_ATTRIBUTES lpSA = NULL; 268 // Custom Security Descriptor is required here to "get" Medium Integrity Level. 269 // In order to allow Medium Integrity Level clients to open 270 // and use a NamedPipe created by an High Integrity Level process. 271 TCHAR *szSD = TEXT("D:") // Discretionary ACL 272 TEXT("(A;OICI;GRGW;;;WD)") // Allow read/write to Everybody 273 TEXT("(A;OICI;GA;;;SY)") // Allow full control to System 274 TEXT("(A;OICI;GA;;;BA)"); // Allow full control to Administrators 275 276 sa.nLength = sizeof(SECURITY_ATTRIBUTES); 277 sa.bInheritHandle = FALSE; 278 sa.lpSecurityDescriptor = NULL; 279 280 if (ConvertStringSecurityDescriptorToSecurityDescriptor 281 (szSD, SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL)) { 282 lpSA = &sa; 283 } 284 285 jstring_to_cstring(env, pipename, name, MAX_PIPE_NAME_LENGTH); 286 287 hPipe = CreateNamedPipe( 288 name, // pipe name 289 PIPE_ACCESS_INBOUND, // read access 290 PIPE_TYPE_BYTE | // byte mode 291 PIPE_READMODE_BYTE | 292 PIPE_WAIT, // blocking mode 293 1, // max. instances 294 128, // output buffer size 295 8192, // input buffer size 296 NMPWAIT_USE_DEFAULT_WAIT, // client time-out 297 lpSA); // security attributes 298 299 LocalFree(sa.lpSecurityDescriptor); 300 301 if (hPipe == INVALID_HANDLE_VALUE) { 302 char msg[256]; 303 _snprintf(msg, sizeof(msg), "CreateNamedPipe failed: %d", GetLastError()); 304 JNU_ThrowIOExceptionWithLastError(env, msg); 305 } 306 return (jlong)hPipe; 307 } 308 309 /* 310 * Class: sun_tools_attach_VirtualMachineImpl 311 * Method: closePipe 312 * Signature: (J)V 313 */ 314 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_closePipe 315 (JNIEnv *env, jclass cls, jlong hPipe) 316 { 317 CloseHandle( (HANDLE)hPipe ); 318 } 319 320 /* 321 * Class: sun_tools_attach_VirtualMachineImpl 322 * Method: connectPipe 323 * Signature: (J)V 324 */ 325 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connectPipe 326 (JNIEnv *env, jclass cls, jlong hPipe) 327 { 328 BOOL fConnected; 329 330 fConnected = ConnectNamedPipe((HANDLE)hPipe, NULL) ? 331 TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); 332 if (!fConnected) { 333 JNU_ThrowIOExceptionWithLastError(env, "ConnectNamedPipe failed"); 334 } 335 } 336 337 /* 338 * Class: sun_tools_attach_VirtualMachineImpl 339 * Method: readPipe 340 * Signature: (J[BII)I 341 */ 342 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_readPipe 343 (JNIEnv *env, jclass cls, jlong hPipe, jbyteArray ba, jint off, jint baLen) 344 { 345 unsigned char buf[128]; 346 DWORD len, nread, remaining; 347 BOOL fSuccess; 348 349 len = sizeof(buf); 350 remaining = (DWORD)(baLen - off); 351 if (len > remaining) { 352 len = remaining; 353 } 354 355 fSuccess = ReadFile( 356 (HANDLE)hPipe, // handle to pipe 357 buf, // buffer to receive data 358 len, // size of buffer 359 &nread, // number of bytes read 360 NULL); // not overlapped I/O 361 362 if (!fSuccess) { 363 if (GetLastError() == ERROR_BROKEN_PIPE) { 364 return (jint)-1; 365 } else { 366 JNU_ThrowIOExceptionWithLastError(env, "ReadFile"); 367 } 368 } else { 369 if (nread == 0) { 370 return (jint)-1; // EOF 371 } else { 372 (*env)->SetByteArrayRegion(env, ba, off, (jint)nread, (jbyte *)(buf)); 373 } 374 } 375 376 return (jint)nread; 377 } 378 379 380 /* 381 * Class: sun_tools_attach_VirtualMachineImpl 382 * Method: enqueue 383 * Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V 384 */ 385 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue 386 (JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd, 387 jstring pipename, jobjectArray args) 388 { 389 DataBlock data; 390 DataBlock* pData; 391 DWORD* pCode; 392 DWORD stubLen; 393 HANDLE hProcess, hThread; 394 jint argsLen, i; 395 jbyte* stubCode; 396 jboolean isCopy; 397 398 /* 399 * Setup data to copy to target process 400 */ 401 data._GetModuleHandle = _GetModuleHandle; 402 data._GetProcAddress = _GetProcAddress; 403 404 strcpy(data.jvmLib, "jvm"); 405 strcpy(data.func1, "JVM_EnqueueOperation"); 406 strcpy(data.func2, "_JVM_EnqueueOperation@20"); 407 408 /* 409 * Command and arguments 410 */ 411 jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH); 412 argsLen = (*env)->GetArrayLength(env, args); 413 414 if (argsLen > 0) { 415 if (argsLen > MAX_ARGS) { 416 JNU_ThrowInternalError(env, "Too many arguments"); 417 return; 418 } 419 for (i=0; i<argsLen; i++) { 420 jobject obj = (*env)->GetObjectArrayElement(env, args, i); 421 if (obj == NULL) { 422 data.arg[i][0] = '\0'; 423 } else { 424 jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH); 425 } 426 if ((*env)->ExceptionOccurred(env)) return; 427 } 428 } 429 for (i=argsLen; i<MAX_ARGS; i++) { 430 data.arg[i][0] = '\0'; 431 } 432 433 /* pipe name */ 434 jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH); 435 436 /* 437 * Allocate memory in target process for data and code stub 438 * (assumed aligned and matches architecture of target process) 439 */ 440 hProcess = (HANDLE)handle; 441 442 pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE ); 443 if (pData == NULL) { 444 JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed"); 445 return; 446 } 447 WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL ); 448 449 450 stubLen = (DWORD)(*env)->GetArrayLength(env, stub); 451 stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy); 452 453 if ((*env)->ExceptionOccurred(env)) return; 454 455 pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); 456 if (pCode == NULL) { 457 JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed"); 458 VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); 459 return; 460 } 461 WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL ); 462 if (isCopy) { 463 (*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT); 464 } 465 466 /* 467 * Create thread in target process to execute code 468 */ 469 hThread = CreateRemoteThread( hProcess, 470 NULL, 471 0, 472 (LPTHREAD_START_ROUTINE) pCode, 473 pData, 474 0, 475 NULL ); 476 if (hThread != NULL) { 477 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) { 478 JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed"); 479 } else { 480 DWORD exitCode; 481 GetExitCodeThread(hThread, &exitCode); 482 if (exitCode) { 483 switch (exitCode) { 484 case ERR_OPEN_JVM_FAIL : 485 JNU_ThrowIOException(env, 486 "jvm.dll not loaded by target process"); 487 break; 488 case ERR_GET_ENQUEUE_FUNC_FAIL : 489 JNU_ThrowIOException(env, 490 "Unable to enqueue operation: the target VM does not support attach mechanism"); 491 break; 492 default : 493 JNU_ThrowInternalError(env, 494 "Remote thread failed for unknown reason"); 495 } 496 } 497 } 498 CloseHandle(hThread); 499 } else { 500 if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { 501 // 502 // This error will occur when attaching to a process belonging to 503 // another terminal session. See "Remarks": 504 // http://msdn.microsoft.com/en-us/library/ms682437%28VS.85%29.aspx 505 // 506 JNU_ThrowIOException(env, 507 "Insufficient memory or insufficient privileges to attach"); 508 } else { 509 JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed"); 510 } 511 } 512 513 VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE); 514 VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); 515 } 516 517 /* 518 * Attempts to enable the SE_DEBUG_NAME privilege and open the given process. 519 */ 520 static HANDLE 521 doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) { 522 HANDLE hToken; 523 HANDLE hProcess = NULL; 524 LUID luid; 525 TOKEN_PRIVILEGES tp, tpPrevious; 526 DWORD retLength, error; 527 528 /* 529 * Get the access token 530 */ 531 if (!OpenThreadToken(GetCurrentThread(), 532 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, 533 FALSE, 534 &hToken)) { 535 if (GetLastError() != ERROR_NO_TOKEN) { 536 return (HANDLE)NULL; 537 } 538 539 /* 540 * No access token for the thread so impersonate the security context 541 * of the process. 542 */ 543 if (!ImpersonateSelf(SecurityImpersonation)) { 544 return (HANDLE)NULL; 545 } 546 if (!OpenThreadToken(GetCurrentThread(), 547 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, 548 FALSE, 549 &hToken)) { 550 return (HANDLE)NULL; 551 } 552 } 553 554 /* 555 * Get LUID for the privilege 556 */ 557 if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) { 558 error = GetLastError(); 559 CloseHandle(hToken); 560 SetLastError(error); 561 return (HANDLE)NULL; 562 } 563 564 /* 565 * Enable the privilege 566 */ 567 ZeroMemory(&tp, sizeof(tp)); 568 tp.PrivilegeCount = 1; 569 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 570 tp.Privileges[0].Luid = luid; 571 572 error = 0; 573 if (AdjustTokenPrivileges(hToken, 574 FALSE, 575 &tp, 576 sizeof(TOKEN_PRIVILEGES), 577 &tpPrevious, 578 &retLength)) { 579 /* 580 * If we enabled the privilege then attempt to open the 581 * process. 582 */ 583 if (GetLastError() == ERROR_SUCCESS) { 584 hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); 585 if (hProcess == NULL) { 586 error = GetLastError(); 587 } 588 } else { 589 error = ERROR_ACCESS_DENIED; 590 } 591 592 /* 593 * Revert to the previous privileges 594 */ 595 AdjustTokenPrivileges(hToken, 596 FALSE, 597 &tpPrevious, 598 retLength, 599 NULL, 600 NULL); 601 } else { 602 error = GetLastError(); 603 } 604 605 606 /* 607 * Close token and restore error 608 */ 609 CloseHandle(hToken); 610 SetLastError(error); 611 612 return hProcess; 613 } 614 615 /* convert jstring to C string */ 616 static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len) { 617 jboolean isCopy; 618 const char* str; 619 620 if (jstr == NULL) { 621 cstr[0] = '\0'; 622 } else { 623 str = JNU_GetStringPlatformChars(env, jstr, &isCopy); 624 if ((*env)->ExceptionOccurred(env)) return; 625 626 strncpy(cstr, str, len); 627 cstr[len-1] = '\0'; 628 if (isCopy) { 629 JNU_ReleaseStringPlatformChars(env, jstr, str); 630 } 631 } 632 }