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 }