1 /* 2 * Copyright (c) 2005, 2015, 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 * Declare library specific JNI_Onload entry if static build 92 */ 93 DEF_STATIC_JNI_OnLoad 94 95 /* 96 * Code copied to target process 97 */ 98 #pragma check_stack (off) 99 /* Switch off all runtime checks (checks caused by /RTC<x>). They cause the 100 * generated code to contain relative jumps to check functions which make 101 * the code position dependent. */ 102 #pragma runtime_checks ("scu", off) 103 DWORD WINAPI jvm_attach_thread_func(DataBlock *pData) 104 { 105 HINSTANCE h; 106 EnqueueOperationFunc addr; 107 108 h = pData->_GetModuleHandle(pData->jvmLib); 109 if (h == NULL) { 110 return ERR_OPEN_JVM_FAIL; 111 } 112 113 addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func1)); 114 if (addr == NULL) { 115 addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func2)); 116 } 117 if (addr == NULL) { 118 return ERR_GET_ENQUEUE_FUNC_FAIL; 119 } 120 121 /* "null" command - does nothing in the target VM */ 122 if (pData->cmd[0] == '\0') { 123 return 0; 124 } else { 125 return (*addr)(pData->cmd, pData->arg[0], pData->arg[1], pData->arg[2], pData->pipename); 126 } 127 } 128 129 /* This function marks the end of jvm_attach_thread_func. */ 130 void jvm_attach_thread_func_end (void) { 131 } 132 #pragma check_stack 133 #pragma runtime_checks ("scu", restore) 134 135 /* 136 * Class: sun_tools_attach_VirtualMachineImpl 137 * Method: init 138 * Signature: ()V 139 */ 140 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_init 141 (JNIEnv *env, jclass cls) 142 { 143 // All following APIs exist on Windows XP with SP2/Windows Server 2008 144 _GetModuleHandle = (GetModuleHandleFunc)GetModuleHandle; 145 _GetProcAddress = (GetProcAddressFunc)GetProcAddress; 146 _IsWow64Process = (IsWow64ProcessFunc)IsWow64Process; 147 } 148 149 150 /* 151 * Class: sun_tools_attach_VirtualMachineImpl 152 * Method: generateStub 153 * Signature: ()[B 154 */ 155 JNIEXPORT jbyteArray JNICALL Java_sun_tools_attach_VirtualMachineImpl_generateStub 156 (JNIEnv *env, jclass cls) 157 { 158 /* 159 * We should replace this with a real stub generator at some point 160 */ 161 DWORD len; 162 jbyteArray array; 163 164 len = (DWORD)((LPBYTE) jvm_attach_thread_func_end - (LPBYTE) jvm_attach_thread_func); 165 array= (*env)->NewByteArray(env, (jsize)len); 166 if (array != NULL) { 167 (*env)->SetByteArrayRegion(env, array, 0, (jint)len, (jbyte*)&jvm_attach_thread_func); 168 } 169 return array; 170 } 171 172 /* 173 * Class: sun_tools_attach_VirtualMachineImpl 174 * Method: openProcess 175 * Signature: (I)J 176 */ 177 JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_openProcess 178 (JNIEnv *env, jclass cls, jint pid) 179 { 180 HANDLE hProcess = NULL; 181 182 if (pid == (jint) GetCurrentProcessId()) { 183 /* process is attaching to itself; get a pseudo handle instead */ 184 hProcess = GetCurrentProcess(); 185 /* duplicate the pseudo handle so it can be used in more contexts */ 186 if (DuplicateHandle(hProcess, hProcess, hProcess, &hProcess, 187 PROCESS_ALL_ACCESS, FALSE, 0) == 0) { 188 /* 189 * Could not duplicate the handle which isn't a good sign, 190 * but we'll try again with OpenProcess() below. 191 */ 192 hProcess = NULL; 193 } 194 } 195 196 if (hProcess == NULL) { 197 /* 198 * Attempt to open process. If it fails then we try to enable the 199 * SE_DEBUG_NAME privilege and retry. 200 */ 201 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); 202 if (hProcess == NULL && GetLastError() == ERROR_ACCESS_DENIED) { 203 hProcess = doPrivilegedOpenProcess(PROCESS_ALL_ACCESS, FALSE, 204 (DWORD)pid); 205 } 206 207 if (hProcess == NULL) { 208 if (GetLastError() == ERROR_INVALID_PARAMETER) { 209 JNU_ThrowIOException(env, "no such process"); 210 } else { 211 char err_mesg[255]; 212 /* include the last error in the default detail message */ 213 sprintf(err_mesg, "OpenProcess(pid=%d) failed; LastError=0x%x", 214 (int)pid, (int)GetLastError()); 215 JNU_ThrowIOExceptionWithLastError(env, err_mesg); 216 } 217 return (jlong)0; 218 } 219 } 220 221 /* 222 * On Windows 64-bit we need to handle 32-bit tools trying to attach to 64-bit 223 * processes (and visa versa). X-architecture attaching is currently not supported 224 * by this implementation. 225 */ 226 if (_IsWow64Process != NULL) { 227 BOOL isCurrent32bit, isTarget32bit; 228 (*_IsWow64Process)(GetCurrentProcess(), &isCurrent32bit); 229 (*_IsWow64Process)(hProcess, &isTarget32bit); 230 231 if (isCurrent32bit != isTarget32bit) { 232 CloseHandle(hProcess); 233 #ifdef _WIN64 234 JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException", 235 "Unable to attach to 32-bit process running under WOW64"); 236 #else 237 JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException", 238 "Unable to attach to 64-bit process"); 239 #endif 240 } 241 } 242 243 return (jlong)hProcess; 244 } 245 246 247 /* 248 * Class: sun_tools_attach_VirtualMachineImpl 249 * Method: closeProcess 250 * Signature: (J)V 251 */ 252 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_closeProcess 253 (JNIEnv *env, jclass cls, jlong hProcess) 254 { 255 CloseHandle((HANDLE)hProcess); 256 } 257 258 259 /* 260 * Class: sun_tools_attach_VirtualMachineImpl 261 * Method: createPipe 262 * Signature: (Ljava/lang/String;)J 263 */ 264 JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_createPipe 265 (JNIEnv *env, jclass cls, jstring pipename) 266 { 267 HANDLE hPipe; 268 char name[MAX_PIPE_NAME_LENGTH]; 269 270 SECURITY_ATTRIBUTES sa; 271 LPSECURITY_ATTRIBUTES lpSA = NULL; 272 // Custom Security Descriptor is required here to "get" Medium Integrity Level. 273 // In order to allow Medium Integrity Level clients to open 274 // and use a NamedPipe created by an High Integrity Level process. 275 TCHAR *szSD = TEXT("D:") // Discretionary ACL 276 TEXT("(A;OICI;GRGW;;;WD)") // Allow read/write to Everybody 277 TEXT("(A;OICI;GA;;;SY)") // Allow full control to System 278 TEXT("(A;OICI;GA;;;BA)"); // Allow full control to Administrators 279 280 sa.nLength = sizeof(SECURITY_ATTRIBUTES); 281 sa.bInheritHandle = FALSE; 282 sa.lpSecurityDescriptor = NULL; 283 284 if (ConvertStringSecurityDescriptorToSecurityDescriptor 285 (szSD, SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL)) { 286 lpSA = &sa; 287 } 288 289 jstring_to_cstring(env, pipename, name, MAX_PIPE_NAME_LENGTH); 290 291 hPipe = CreateNamedPipe( 292 name, // pipe name 293 PIPE_ACCESS_INBOUND, // read access 294 PIPE_TYPE_BYTE | // byte mode 295 PIPE_READMODE_BYTE | 296 PIPE_WAIT, // blocking mode 297 1, // max. instances 298 128, // output buffer size 299 8192, // input buffer size 300 NMPWAIT_USE_DEFAULT_WAIT, // client time-out 301 lpSA); // security attributes 302 303 LocalFree(sa.lpSecurityDescriptor); 304 305 if (hPipe == INVALID_HANDLE_VALUE) { 306 char msg[256]; 307 _snprintf(msg, sizeof(msg), "CreateNamedPipe failed: %d", GetLastError()); 308 JNU_ThrowIOExceptionWithLastError(env, msg); 309 } 310 return (jlong)hPipe; 311 } 312 313 /* 314 * Class: sun_tools_attach_VirtualMachineImpl 315 * Method: closePipe 316 * Signature: (J)V 317 */ 318 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_closePipe 319 (JNIEnv *env, jclass cls, jlong hPipe) 320 { 321 CloseHandle( (HANDLE)hPipe ); 322 } 323 324 /* 325 * Class: sun_tools_attach_VirtualMachineImpl 326 * Method: connectPipe 327 * Signature: (J)V 328 */ 329 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connectPipe 330 (JNIEnv *env, jclass cls, jlong hPipe) 331 { 332 BOOL fConnected; 333 334 fConnected = ConnectNamedPipe((HANDLE)hPipe, NULL) ? 335 TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); 336 if (!fConnected) { 337 JNU_ThrowIOExceptionWithLastError(env, "ConnectNamedPipe failed"); 338 } 339 } 340 341 /* 342 * Class: sun_tools_attach_VirtualMachineImpl 343 * Method: readPipe 344 * Signature: (J[BII)I 345 */ 346 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_readPipe 347 (JNIEnv *env, jclass cls, jlong hPipe, jbyteArray ba, jint off, jint baLen) 348 { 349 unsigned char buf[128]; 350 DWORD len, nread, remaining; 351 BOOL fSuccess; 352 353 len = sizeof(buf); 354 remaining = (DWORD)(baLen - off); 355 if (len > remaining) { 356 len = remaining; 357 } 358 359 fSuccess = ReadFile( 360 (HANDLE)hPipe, // handle to pipe 361 buf, // buffer to receive data 362 len, // size of buffer 363 &nread, // number of bytes read 364 NULL); // not overlapped I/O 365 366 if (!fSuccess) { 367 if (GetLastError() == ERROR_BROKEN_PIPE) { 368 return (jint)-1; 369 } else { 370 JNU_ThrowIOExceptionWithLastError(env, "ReadFile"); 371 } 372 } else { 373 if (nread == 0) { 374 return (jint)-1; // EOF 375 } else { 376 (*env)->SetByteArrayRegion(env, ba, off, (jint)nread, (jbyte *)(buf)); 377 } 378 } 379 380 return (jint)nread; 381 } 382 383 384 /* 385 * Class: sun_tools_attach_VirtualMachineImpl 386 * Method: enqueue 387 * Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V 388 */ 389 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue 390 (JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd, 391 jstring pipename, jobjectArray args) 392 { 393 DataBlock data; 394 DataBlock* pData; 395 DWORD* pCode; 396 DWORD stubLen; 397 HANDLE hProcess, hThread; 398 jint argsLen, i; 399 jbyte* stubCode; 400 jboolean isCopy; 401 402 /* 403 * Setup data to copy to target process 404 */ 405 data._GetModuleHandle = _GetModuleHandle; 406 data._GetProcAddress = _GetProcAddress; 407 408 strcpy(data.jvmLib, "jvm"); 409 strcpy(data.func1, "JVM_EnqueueOperation"); 410 strcpy(data.func2, "_JVM_EnqueueOperation@20"); 411 412 /* 413 * Command and arguments 414 */ 415 jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH); 416 argsLen = (*env)->GetArrayLength(env, args); 417 418 if (argsLen > 0) { 419 if (argsLen > MAX_ARGS) { 420 JNU_ThrowInternalError(env, "Too many arguments"); 421 return; 422 } 423 for (i=0; i<argsLen; i++) { 424 jobject obj = (*env)->GetObjectArrayElement(env, args, i); 425 if (obj == NULL) { 426 data.arg[i][0] = '\0'; 427 } else { 428 jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH); 429 } 430 if ((*env)->ExceptionOccurred(env)) return; 431 } 432 } 433 for (i=argsLen; i<MAX_ARGS; i++) { 434 data.arg[i][0] = '\0'; 435 } 436 437 /* pipe name */ 438 jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH); 439 440 /* 441 * Allocate memory in target process for data and code stub 442 * (assumed aligned and matches architecture of target process) 443 */ 444 hProcess = (HANDLE)handle; 445 446 pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE ); 447 if (pData == NULL) { 448 JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed"); 449 return; 450 } 451 WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL ); 452 453 454 stubLen = (DWORD)(*env)->GetArrayLength(env, stub); 455 stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy); 456 457 if ((*env)->ExceptionOccurred(env)) return; 458 459 pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); 460 if (pCode == NULL) { 461 JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed"); 462 VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); 463 return; 464 } 465 WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL ); 466 if (isCopy) { 467 (*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT); 468 } 469 470 /* 471 * Create thread in target process to execute code 472 */ 473 hThread = CreateRemoteThread( hProcess, 474 NULL, 475 0, 476 (LPTHREAD_START_ROUTINE) pCode, 477 pData, 478 0, 479 NULL ); 480 if (hThread != NULL) { 481 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) { 482 JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed"); 483 } else { 484 DWORD exitCode; 485 GetExitCodeThread(hThread, &exitCode); 486 if (exitCode) { 487 switch (exitCode) { 488 case ERR_OPEN_JVM_FAIL : 489 JNU_ThrowIOException(env, 490 "jvm.dll not loaded by target process"); 491 break; 492 case ERR_GET_ENQUEUE_FUNC_FAIL : 493 JNU_ThrowIOException(env, 494 "Unable to enqueue operation: the target VM does not support attach mechanism"); 495 break; 496 default : 497 JNU_ThrowInternalError(env, 498 "Remote thread failed for unknown reason"); 499 } 500 } 501 } 502 CloseHandle(hThread); 503 } else { 504 if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { 505 // 506 // This error will occur when attaching to a process belonging to 507 // another terminal session. See "Remarks": 508 // http://msdn.microsoft.com/en-us/library/ms682437%28VS.85%29.aspx 509 // 510 JNU_ThrowIOException(env, 511 "Insufficient memory or insufficient privileges to attach"); 512 } else { 513 JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed"); 514 } 515 } 516 517 VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE); 518 VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); 519 } 520 521 /* 522 * Attempts to enable the SE_DEBUG_NAME privilege and open the given process. 523 */ 524 static HANDLE 525 doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) { 526 HANDLE hToken; 527 HANDLE hProcess = NULL; 528 LUID luid; 529 TOKEN_PRIVILEGES tp, tpPrevious; 530 DWORD retLength, error; 531 532 /* 533 * Get the access token 534 */ 535 if (!OpenThreadToken(GetCurrentThread(), 536 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, 537 FALSE, 538 &hToken)) { 539 if (GetLastError() != ERROR_NO_TOKEN) { 540 return (HANDLE)NULL; 541 } 542 543 /* 544 * No access token for the thread so impersonate the security context 545 * of the process. 546 */ 547 if (!ImpersonateSelf(SecurityImpersonation)) { 548 return (HANDLE)NULL; 549 } 550 if (!OpenThreadToken(GetCurrentThread(), 551 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, 552 FALSE, 553 &hToken)) { 554 return (HANDLE)NULL; 555 } 556 } 557 558 /* 559 * Get LUID for the privilege 560 */ 561 if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) { 562 error = GetLastError(); 563 CloseHandle(hToken); 564 SetLastError(error); 565 return (HANDLE)NULL; 566 } 567 568 /* 569 * Enable the privilege 570 */ 571 ZeroMemory(&tp, sizeof(tp)); 572 tp.PrivilegeCount = 1; 573 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 574 tp.Privileges[0].Luid = luid; 575 576 error = 0; 577 if (AdjustTokenPrivileges(hToken, 578 FALSE, 579 &tp, 580 sizeof(TOKEN_PRIVILEGES), 581 &tpPrevious, 582 &retLength)) { 583 /* 584 * If we enabled the privilege then attempt to open the 585 * process. 586 */ 587 if (GetLastError() == ERROR_SUCCESS) { 588 hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); 589 if (hProcess == NULL) { 590 error = GetLastError(); 591 } 592 } else { 593 error = ERROR_ACCESS_DENIED; 594 } 595 596 /* 597 * Revert to the previous privileges 598 */ 599 AdjustTokenPrivileges(hToken, 600 FALSE, 601 &tpPrevious, 602 retLength, 603 NULL, 604 NULL); 605 } else { 606 error = GetLastError(); 607 } 608 609 610 /* 611 * Close token and restore error 612 */ 613 CloseHandle(hToken); 614 SetLastError(error); 615 616 return hProcess; 617 } 618 619 /* convert jstring to C string */ 620 static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len) { 621 jboolean isCopy; 622 const char* str; 623 624 if (jstr == NULL) { 625 cstr[0] = '\0'; 626 } else { 627 str = JNU_GetStringPlatformChars(env, jstr, &isCopy); 628 if ((*env)->ExceptionOccurred(env)) return; 629 630 strncpy(cstr, str, len); 631 cstr[len-1] = '\0'; 632 if (isCopy) { 633 JNU_ReleaseStringPlatformChars(env, jstr, str); 634 } 635 } 636 }