1 /*
   2  * Copyright (c) 2005, 2006, 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;
 177 
 178     /*
 179      * Attempt to open process. If it fails then we try to enable the
 180      * SE_DEBUG_NAME privilege and retry.
 181      */
 182     hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
 183     if (hProcess == NULL && GetLastError() == ERROR_ACCESS_DENIED) {
 184         hProcess = doPrivilegedOpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
 185     }
 186 
 187     if (hProcess == NULL) {
 188         if (GetLastError() == ERROR_INVALID_PARAMETER) {
 189             JNU_ThrowIOException(env, "no such process");
 190         } else {
 191             JNU_ThrowIOExceptionWithLastError(env, "OpenProcess failed");
 192         }
 193         return (jlong)0;
 194     }
 195 
 196     /*
 197      * On Windows 64-bit we need to handle 32-bit tools trying to attach to 64-bit
 198      * processes (and visa versa). X-architecture attaching is currently not supported
 199      * by this implementation.
 200      */
 201     if (_IsWow64Process != NULL) {
 202         BOOL isCurrent32bit, isTarget32bit;
 203         (*_IsWow64Process)(GetCurrentProcess(), &isCurrent32bit);
 204         (*_IsWow64Process)(hProcess, &isTarget32bit);
 205 
 206         if (isCurrent32bit != isTarget32bit) {
 207             CloseHandle(hProcess);
 208             #ifdef _WIN64
 209               JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",
 210                   "Unable to attach to 32-bit process running under WOW64");
 211             #else
 212               JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",
 213                   "Unable to attach to 64-bit process");
 214             #endif
 215         }
 216     }
 217 
 218     return (jlong)hProcess;
 219 }
 220 
 221 
 222 /*
 223  * Class:     sun_tools_attach_WindowsVirtualMachine
 224  * Method:    closeProcess
 225  * Signature: (J)V
 226  */
 227 JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_closeProcess
 228   (JNIEnv *env, jclass cls, jlong hProcess)
 229 {
 230     CloseHandle((HANDLE)hProcess);
 231 }
 232 
 233 
 234 /*
 235  * Class:     sun_tools_attach_WindowsVirtualMachine
 236  * Method:    createPipe
 237  * Signature: (Ljava/lang/String;)J
 238  */
 239 JNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_createPipe
 240   (JNIEnv *env, jclass cls, jstring pipename)
 241 {
 242     HANDLE hPipe;
 243     char name[MAX_PIPE_NAME_LENGTH];
 244 
 245     jstring_to_cstring(env, pipename, name, MAX_PIPE_NAME_LENGTH);
 246 
 247     hPipe = CreateNamedPipe(
 248           name,                         // pipe name
 249           PIPE_ACCESS_INBOUND,          // read access
 250           PIPE_TYPE_BYTE |              // byte mode
 251             PIPE_READMODE_BYTE |
 252             PIPE_WAIT,                  // blocking mode
 253           1,                            // max. instances
 254           128,                          // output buffer size
 255           8192,                         // input buffer size
 256           NMPWAIT_USE_DEFAULT_WAIT,     // client time-out
 257           NULL);                        // default security attribute
 258 
 259     if (hPipe == INVALID_HANDLE_VALUE) {
 260         JNU_ThrowIOExceptionWithLastError(env, "CreateNamedPipe failed");
 261     }
 262     return (jlong)hPipe;
 263 }
 264 
 265 /*
 266  * Class:     sun_tools_attach_WindowsVirtualMachine
 267  * Method:    closePipe
 268  * Signature: (J)V
 269  */
 270 JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_closePipe
 271   (JNIEnv *env, jclass cls, jlong hPipe)
 272 {
 273     CloseHandle( (HANDLE)hPipe );
 274 }
 275 
 276 /*
 277  * Class:     sun_tools_attach_WindowsVirtualMachine
 278  * Method:    connectPipe
 279  * Signature: (J)V
 280  */
 281 JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_connectPipe
 282   (JNIEnv *env, jclass cls, jlong hPipe)
 283 {
 284     BOOL fConnected;
 285 
 286     fConnected = ConnectNamedPipe((HANDLE)hPipe, NULL) ?
 287         TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
 288     if (!fConnected) {
 289         JNU_ThrowIOExceptionWithLastError(env, "ConnectNamedPipe failed");
 290     }
 291 }
 292 
 293 /*
 294  * Class:     sun_tools_attach_WindowsVirtualMachine
 295  * Method:    readPipe
 296  * Signature: (J[BII)I
 297  */
 298 JNIEXPORT jint JNICALL Java_sun_tools_attach_WindowsVirtualMachine_readPipe
 299   (JNIEnv *env, jclass cls, jlong hPipe, jbyteArray ba, jint off, jint baLen)
 300 {
 301     unsigned char buf[128];
 302     DWORD len, nread, remaining;
 303     BOOL fSuccess;
 304 
 305     len = sizeof(buf);
 306     remaining = (DWORD)(baLen - off);
 307     if (len > remaining) {
 308         len = remaining;
 309     }
 310 
 311     fSuccess = ReadFile(
 312          (HANDLE)hPipe,         // handle to pipe
 313          buf,                   // buffer to receive data
 314          len,                   // size of buffer
 315          &nread,                // number of bytes read
 316          NULL);                 // not overlapped I/O
 317 
 318     if (!fSuccess) {
 319         if (GetLastError() == ERROR_BROKEN_PIPE) {
 320             return (jint)-1;
 321         } else {
 322             JNU_ThrowIOExceptionWithLastError(env, "ReadFile");
 323         }
 324     } else {
 325         if (nread == 0) {
 326             return (jint)-1;        // EOF
 327         } else {
 328             (*env)->SetByteArrayRegion(env, ba, off, (jint)nread, (jbyte *)(buf+off));
 329         }
 330     }
 331 
 332     return (jint)nread;
 333 }
 334 
 335 
 336 /*
 337  * Class:     sun_tools_attach_WindowsVirtualMachine
 338  * Method:    enqueue
 339  * Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V
 340  */
 341 JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_enqueue
 342   (JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd,
 343    jstring pipename, jobjectArray args)
 344 {
 345     DataBlock data;
 346     DataBlock* pData;
 347     DWORD* pCode;
 348     DWORD stubLen;
 349     HANDLE hProcess, hThread;
 350     jint argsLen, i;
 351     jbyte* stubCode;
 352     jboolean isCopy;
 353 
 354     /*
 355      * Setup data to copy to target process
 356      */
 357     data._LoadLibrary = _LoadLibrary;
 358     data._GetProcAddress = _GetProcAddress;
 359 
 360     strcpy(data.jvmLib, "jvm");
 361     strcpy(data.func1, "JVM_EnqueueOperation");
 362     strcpy(data.func2, "_JVM_EnqueueOperation@20");
 363 
 364     /*
 365      * Command and arguments
 366      */
 367     jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH);
 368     argsLen = (*env)->GetArrayLength(env, args);
 369 
 370     if (argsLen > 0) {
 371         if (argsLen > MAX_ARGS) {
 372             JNU_ThrowInternalError(env, "Too many arguments");
 373         }
 374         for (i=0; i<argsLen; i++) {
 375             jobject obj = (*env)->GetObjectArrayElement(env, args, i);
 376             if (obj == NULL) {
 377                 data.arg[i][0] = '\0';
 378             } else {
 379                 jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH);
 380             }
 381             if ((*env)->ExceptionOccurred(env)) return;
 382         }
 383     }
 384     for (i=argsLen; i<MAX_ARGS; i++) {
 385         data.arg[i][0] = '\0';
 386     }
 387 
 388     /* pipe name */
 389     jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH);
 390 
 391     /*
 392      * Allocate memory in target process for data and code stub
 393      * (assumed aligned and matches architecture of target process)
 394      */
 395     hProcess = (HANDLE)handle;
 396 
 397     pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE );
 398     if (pData == NULL) {
 399         JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
 400         return;
 401     }
 402     WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL );
 403 
 404 
 405     stubLen = (DWORD)(*env)->GetArrayLength(env, stub);
 406     stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy);
 407 
 408     pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
 409     if (pCode == NULL) {
 410         JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
 411         VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
 412         return;
 413     }
 414     WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL );
 415     if (isCopy) {
 416         (*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);
 417     }
 418 
 419     /*
 420      * Create thread in target process to execute code
 421      */
 422     hThread = CreateRemoteThread( hProcess,
 423                                   NULL,
 424                                   0,
 425                                   (LPTHREAD_START_ROUTINE) pCode,
 426                                   pData,
 427                                   0,
 428                                   NULL );
 429     if (hThread != NULL) {
 430         if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) {
 431             JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed");
 432         } else {
 433             DWORD exitCode;
 434             GetExitCodeThread(hThread, &exitCode);
 435             if (exitCode) {
 436                 switch (exitCode) {
 437                     case ERR_OPEN_JVM_FAIL :
 438                         JNU_ThrowIOException(env,
 439                             "jvm.dll not loaded by target process");
 440                         break;
 441                     case ERR_GET_ENQUEUE_FUNC_FAIL :
 442                         JNU_ThrowIOException(env,
 443                             "Unable to enqueue operation: the target VM does not support attach mechanism");
 444                         break;
 445                     default :
 446                         JNU_ThrowInternalError(env,
 447                             "Remote thread failed for unknown reason");
 448                 }
 449             }
 450         }
 451         CloseHandle(hThread);
 452     } else {
 453         JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed");
 454     }
 455 
 456     VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE);
 457     VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
 458 }
 459 
 460 /*
 461  * Attempts to enable the SE_DEBUG_NAME privilege and open the given process.
 462  */
 463 static HANDLE
 464 doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) {
 465     HANDLE hToken;
 466     HANDLE hProcess = NULL;
 467     LUID luid;
 468     TOKEN_PRIVILEGES tp, tpPrevious;
 469     DWORD retLength, error;
 470 
 471     /*
 472      * Get the access token
 473      */
 474     if (!OpenThreadToken(GetCurrentThread(),
 475                          TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
 476                          FALSE,
 477                          &hToken)) {
 478         if (GetLastError() != ERROR_NO_TOKEN) {
 479             return (HANDLE)NULL;
 480         }
 481 
 482         /*
 483          * No access token for the thread so impersonate the security context
 484          * of the process.
 485          */
 486         if (!ImpersonateSelf(SecurityImpersonation)) {
 487             return (HANDLE)NULL;
 488         }
 489         if (!OpenThreadToken(GetCurrentThread(),
 490                              TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
 491                              FALSE,
 492                              &hToken)) {
 493             return (HANDLE)NULL;
 494         }
 495     }
 496 
 497     /*
 498      * Get LUID for the privilege
 499      */
 500     if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
 501         error = GetLastError();
 502         CloseHandle(hToken);
 503         SetLastError(error);
 504         return (HANDLE)NULL;
 505     }
 506 
 507     /*
 508      * Enable the privilege
 509      */
 510     ZeroMemory(&tp, sizeof(tp));
 511     tp.PrivilegeCount = 1;
 512     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
 513     tp.Privileges[0].Luid = luid;
 514 
 515     error = 0;
 516     if (AdjustTokenPrivileges(hToken,
 517                               FALSE,
 518                               &tp,
 519                               sizeof(TOKEN_PRIVILEGES),
 520                               &tpPrevious,
 521                               &retLength)) {
 522         /*
 523          * If we enabled the privilege then attempt to open the
 524          * process.
 525          */
 526         if (GetLastError() == ERROR_SUCCESS) {
 527             hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
 528             if (hProcess == NULL) {
 529                 error = GetLastError();
 530             }
 531         } else {
 532             error = ERROR_ACCESS_DENIED;
 533         }
 534 
 535         /*
 536          * Revert to the previous privileges
 537          */
 538         AdjustTokenPrivileges(hToken,
 539                               FALSE,
 540                               &tpPrevious,
 541                               retLength,
 542                               NULL,
 543                               NULL);
 544     } else {
 545         error = GetLastError();
 546     }
 547 
 548 
 549     /*
 550      * Close token and restore error
 551      */
 552     CloseHandle(hToken);
 553     SetLastError(error);
 554 
 555     return hProcess;
 556 }
 557 
 558 /* convert jstring to C string */
 559 static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len) {
 560     jboolean isCopy;
 561     const char* str;
 562 
 563     if (jstr == NULL) {
 564         cstr[0] = '\0';
 565     } else {
 566         str = JNU_GetStringPlatformChars(env, jstr, &isCopy);
 567         strncpy(cstr, str, len);
 568         cstr[len-1] = '\0';
 569         if (isCopy) {
 570             JNU_ReleaseStringPlatformChars(env, jstr, str);
 571         }
 572     }
 573 }