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 }