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 }