0N/A/*
3775N/A * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A#include <windows.h>
0N/A#include <string.h>
0N/A
0N/A#include "jni.h"
0N/A#include "jni_util.h"
0N/A
0N/A#include "sun_tools_attach_WindowsVirtualMachine.h"
0N/A
0N/A
0N/A/* kernel32 */
4311N/Atypedef HINSTANCE (WINAPI* GetModuleHandleFunc) (LPCTSTR);
0N/Atypedef FARPROC (WINAPI* GetProcAddressFunc)(HMODULE, LPCSTR);
0N/A
0N/A/* only on Windows 64-bit or 32-bit application running under WOW64 */
0N/Atypedef BOOL (WINAPI *IsWow64ProcessFunc) (HANDLE, PBOOL);
0N/A
4311N/Astatic GetModuleHandleFunc _GetModuleHandle;
0N/Astatic GetProcAddressFunc _GetProcAddress;
0N/Astatic IsWow64ProcessFunc _IsWow64Process;
0N/A
0N/A/* psapi */
0N/Atypedef BOOL (WINAPI *EnumProcessModulesFunc) (HANDLE, HMODULE *, DWORD, LPDWORD );
0N/Atypedef DWORD (WINAPI *GetModuleFileNameExFunc) ( HANDLE, HMODULE, LPTSTR, DWORD );
0N/A
0N/A/* exported function in target VM */
0N/Atypedef jint (WINAPI* EnqueueOperationFunc)
0N/A (const char* cmd, const char* arg1, const char* arg2, const char* arg3, const char* pipename);
0N/A
0N/A/* OpenProcess with SE_DEBUG_NAME privilege */
0N/Astatic HANDLE
0N/AdoPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);
0N/A
0N/A/* convert jstring to C string */
0N/Astatic void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len);
0N/A
0N/A
0N/A/*
0N/A * Data copied to target process
0N/A */
0N/A
0N/A#define MAX_LIBNAME_LENGTH 16
0N/A#define MAX_FUNC_LENGTH 32
0N/A#define MAX_CMD_LENGTH 16
0N/A#define MAX_ARG_LENGTH 1024
0N/A#define MAX_ARGS 3
0N/A#define MAX_PIPE_NAME_LENGTH 256
0N/A
0N/Atypedef struct {
4311N/A GetModuleHandleFunc _GetModuleHandle;
0N/A GetProcAddressFunc _GetProcAddress;
0N/A char jvmLib[MAX_LIBNAME_LENGTH]; /* "jvm.dll" */
0N/A char func1[MAX_FUNC_LENGTH];
0N/A char func2[MAX_FUNC_LENGTH];
0N/A char cmd[MAX_CMD_LENGTH]; /* "load", "dump", ... */
0N/A char arg[MAX_ARGS][MAX_ARG_LENGTH]; /* arguments to command */
0N/A char pipename[MAX_PIPE_NAME_LENGTH];
0N/A} DataBlock;
0N/A
0N/A/*
0N/A * Return codes from enqueue function executed in target VM
0N/A */
0N/A#define ERR_OPEN_JVM_FAIL 200
0N/A#define ERR_GET_ENQUEUE_FUNC_FAIL 201
0N/A
0N/A
0N/A/*
0N/A * Code copied to target process
0N/A */
0N/A#pragma check_stack (off)
0N/Astatic DWORD WINAPI thread_func(DataBlock *pData)
0N/A{
0N/A HINSTANCE h;
0N/A EnqueueOperationFunc addr;
0N/A
4311N/A h = pData->_GetModuleHandle(pData->jvmLib);
0N/A if (h == NULL) {
0N/A return ERR_OPEN_JVM_FAIL;
0N/A }
0N/A
0N/A addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func1));
0N/A if (addr == NULL) {
0N/A addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func2));
0N/A }
0N/A if (addr == NULL) {
0N/A return ERR_GET_ENQUEUE_FUNC_FAIL;
0N/A }
0N/A
0N/A /* "null" command - does nothing in the target VM */
0N/A if (pData->cmd[0] == '\0') {
0N/A return 0;
0N/A } else {
0N/A return (*addr)(pData->cmd, pData->arg[0], pData->arg[1], pData->arg[2], pData->pipename);
0N/A }
0N/A}
0N/A
0N/A/* This function marks the end of thread_func. */
0N/Astatic void thread_end (void) {
0N/A}
0N/A#pragma check_stack
0N/A
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_WindowsVirtualMachine
0N/A * Method: init
0N/A * Signature: ()V
0N/A */
0N/AJNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_init
0N/A (JNIEnv *env, jclass cls)
0N/A{
4311N/A // All following APIs exist on Windows XP with SP2/Windows Server 2008
4311N/A _GetModuleHandle = (GetModuleHandleFunc)GetModuleHandle;
4311N/A _GetProcAddress = (GetProcAddressFunc)GetProcAddress;
4311N/A _IsWow64Process = (IsWow64ProcessFunc)IsWow64Process;
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_WindowsVirtualMachine
0N/A * Method: generateStub
0N/A * Signature: ()[B
0N/A */
0N/AJNIEXPORT jbyteArray JNICALL Java_sun_tools_attach_WindowsVirtualMachine_generateStub
0N/A (JNIEnv *env, jclass cls)
0N/A{
0N/A /*
0N/A * We should replace this with a real stub generator at some point
0N/A */
0N/A DWORD len;
0N/A jbyteArray array;
0N/A
0N/A len = (DWORD)((LPBYTE) thread_end - (LPBYTE) thread_func);
0N/A array= (*env)->NewByteArray(env, (jsize)len);
0N/A if (array != NULL) {
0N/A (*env)->SetByteArrayRegion(env, array, 0, (jint)len, (jbyte*)&thread_func);
0N/A }
0N/A return array;
0N/A}
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_WindowsVirtualMachine
0N/A * Method: openProcess
0N/A * Signature: (I)J
0N/A */
0N/AJNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_openProcess
0N/A (JNIEnv *env, jclass cls, jint pid)
0N/A{
3775N/A HANDLE hProcess = NULL;
0N/A
3775N/A if (pid == (jint) GetCurrentProcessId()) {
3775N/A /* process is attaching to itself; get a pseudo handle instead */
3775N/A hProcess = GetCurrentProcess();
3775N/A /* duplicate the pseudo handle so it can be used in more contexts */
3775N/A if (DuplicateHandle(hProcess, hProcess, hProcess, &hProcess,
3775N/A PROCESS_ALL_ACCESS, FALSE, 0) == 0) {
3775N/A /*
3775N/A * Could not duplicate the handle which isn't a good sign,
3775N/A * but we'll try again with OpenProcess() below.
3775N/A */
3775N/A hProcess = NULL;
3775N/A }
0N/A }
0N/A
0N/A if (hProcess == NULL) {
3775N/A /*
3775N/A * Attempt to open process. If it fails then we try to enable the
3775N/A * SE_DEBUG_NAME privilege and retry.
3775N/A */
3775N/A hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
3775N/A if (hProcess == NULL && GetLastError() == ERROR_ACCESS_DENIED) {
3775N/A hProcess = doPrivilegedOpenProcess(PROCESS_ALL_ACCESS, FALSE,
3775N/A (DWORD)pid);
0N/A }
3775N/A
3775N/A if (hProcess == NULL) {
3775N/A if (GetLastError() == ERROR_INVALID_PARAMETER) {
3775N/A JNU_ThrowIOException(env, "no such process");
3775N/A } else {
3775N/A char err_mesg[255];
3775N/A /* include the last error in the default detail message */
3775N/A sprintf(err_mesg, "OpenProcess(pid=%d) failed; LastError=0x%x",
3775N/A (int)pid, (int)GetLastError());
3775N/A JNU_ThrowIOExceptionWithLastError(env, err_mesg);
3775N/A }
3775N/A return (jlong)0;
3775N/A }
0N/A }
0N/A
0N/A /*
0N/A * On Windows 64-bit we need to handle 32-bit tools trying to attach to 64-bit
0N/A * processes (and visa versa). X-architecture attaching is currently not supported
0N/A * by this implementation.
0N/A */
0N/A if (_IsWow64Process != NULL) {
0N/A BOOL isCurrent32bit, isTarget32bit;
0N/A (*_IsWow64Process)(GetCurrentProcess(), &isCurrent32bit);
0N/A (*_IsWow64Process)(hProcess, &isTarget32bit);
0N/A
0N/A if (isCurrent32bit != isTarget32bit) {
0N/A CloseHandle(hProcess);
0N/A #ifdef _WIN64
0N/A JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",
0N/A "Unable to attach to 32-bit process running under WOW64");
0N/A #else
0N/A JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",
0N/A "Unable to attach to 64-bit process");
0N/A #endif
0N/A }
0N/A }
0N/A
0N/A return (jlong)hProcess;
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_WindowsVirtualMachine
0N/A * Method: closeProcess
0N/A * Signature: (J)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_closeProcess
0N/A (JNIEnv *env, jclass cls, jlong hProcess)
0N/A{
0N/A CloseHandle((HANDLE)hProcess);
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_WindowsVirtualMachine
0N/A * Method: createPipe
0N/A * Signature: (Ljava/lang/String;)J
0N/A */
0N/AJNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_createPipe
0N/A (JNIEnv *env, jclass cls, jstring pipename)
0N/A{
0N/A HANDLE hPipe;
0N/A char name[MAX_PIPE_NAME_LENGTH];
0N/A
0N/A jstring_to_cstring(env, pipename, name, MAX_PIPE_NAME_LENGTH);
0N/A
0N/A hPipe = CreateNamedPipe(
0N/A name, // pipe name
0N/A PIPE_ACCESS_INBOUND, // read access
0N/A PIPE_TYPE_BYTE | // byte mode
0N/A PIPE_READMODE_BYTE |
0N/A PIPE_WAIT, // blocking mode
0N/A 1, // max. instances
0N/A 128, // output buffer size
0N/A 8192, // input buffer size
0N/A NMPWAIT_USE_DEFAULT_WAIT, // client time-out
0N/A NULL); // default security attribute
0N/A
0N/A if (hPipe == INVALID_HANDLE_VALUE) {
0N/A JNU_ThrowIOExceptionWithLastError(env, "CreateNamedPipe failed");
0N/A }
0N/A return (jlong)hPipe;
0N/A}
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_WindowsVirtualMachine
0N/A * Method: closePipe
0N/A * Signature: (J)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_closePipe
0N/A (JNIEnv *env, jclass cls, jlong hPipe)
0N/A{
0N/A CloseHandle( (HANDLE)hPipe );
0N/A}
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_WindowsVirtualMachine
0N/A * Method: connectPipe
0N/A * Signature: (J)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_connectPipe
0N/A (JNIEnv *env, jclass cls, jlong hPipe)
0N/A{
0N/A BOOL fConnected;
0N/A
0N/A fConnected = ConnectNamedPipe((HANDLE)hPipe, NULL) ?
0N/A TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
0N/A if (!fConnected) {
0N/A JNU_ThrowIOExceptionWithLastError(env, "ConnectNamedPipe failed");
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_WindowsVirtualMachine
0N/A * Method: readPipe
0N/A * Signature: (J[BII)I
0N/A */
0N/AJNIEXPORT jint JNICALL Java_sun_tools_attach_WindowsVirtualMachine_readPipe
0N/A (JNIEnv *env, jclass cls, jlong hPipe, jbyteArray ba, jint off, jint baLen)
0N/A{
0N/A unsigned char buf[128];
0N/A DWORD len, nread, remaining;
0N/A BOOL fSuccess;
0N/A
0N/A len = sizeof(buf);
0N/A remaining = (DWORD)(baLen - off);
0N/A if (len > remaining) {
0N/A len = remaining;
0N/A }
0N/A
0N/A fSuccess = ReadFile(
0N/A (HANDLE)hPipe, // handle to pipe
0N/A buf, // buffer to receive data
0N/A len, // size of buffer
0N/A &nread, // number of bytes read
0N/A NULL); // not overlapped I/O
0N/A
0N/A if (!fSuccess) {
0N/A if (GetLastError() == ERROR_BROKEN_PIPE) {
0N/A return (jint)-1;
0N/A } else {
0N/A JNU_ThrowIOExceptionWithLastError(env, "ReadFile");
0N/A }
0N/A } else {
0N/A if (nread == 0) {
0N/A return (jint)-1; // EOF
0N/A } else {
0N/A (*env)->SetByteArrayRegion(env, ba, off, (jint)nread, (jbyte *)(buf+off));
0N/A }
0N/A }
0N/A
0N/A return (jint)nread;
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Class: sun_tools_attach_WindowsVirtualMachine
0N/A * Method: enqueue
0N/A * Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_enqueue
0N/A (JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd,
0N/A jstring pipename, jobjectArray args)
0N/A{
0N/A DataBlock data;
0N/A DataBlock* pData;
0N/A DWORD* pCode;
0N/A DWORD stubLen;
0N/A HANDLE hProcess, hThread;
0N/A jint argsLen, i;
0N/A jbyte* stubCode;
0N/A jboolean isCopy;
0N/A
0N/A /*
0N/A * Setup data to copy to target process
0N/A */
4311N/A data._GetModuleHandle = _GetModuleHandle;
0N/A data._GetProcAddress = _GetProcAddress;
0N/A
0N/A strcpy(data.jvmLib, "jvm");
0N/A strcpy(data.func1, "JVM_EnqueueOperation");
0N/A strcpy(data.func2, "_JVM_EnqueueOperation@20");
0N/A
0N/A /*
0N/A * Command and arguments
0N/A */
0N/A jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH);
0N/A argsLen = (*env)->GetArrayLength(env, args);
0N/A
0N/A if (argsLen > 0) {
0N/A if (argsLen > MAX_ARGS) {
0N/A JNU_ThrowInternalError(env, "Too many arguments");
0N/A }
0N/A for (i=0; i<argsLen; i++) {
0N/A jobject obj = (*env)->GetObjectArrayElement(env, args, i);
0N/A if (obj == NULL) {
0N/A data.arg[i][0] = '\0';
0N/A } else {
0N/A jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH);
0N/A }
0N/A if ((*env)->ExceptionOccurred(env)) return;
0N/A }
0N/A }
0N/A for (i=argsLen; i<MAX_ARGS; i++) {
0N/A data.arg[i][0] = '\0';
0N/A }
0N/A
0N/A /* pipe name */
0N/A jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH);
0N/A
0N/A /*
0N/A * Allocate memory in target process for data and code stub
0N/A * (assumed aligned and matches architecture of target process)
0N/A */
0N/A hProcess = (HANDLE)handle;
0N/A
0N/A pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE );
0N/A if (pData == NULL) {
0N/A JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
0N/A return;
0N/A }
784N/A WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL );
0N/A
0N/A
0N/A stubLen = (DWORD)(*env)->GetArrayLength(env, stub);
0N/A stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy);
0N/A
0N/A pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
0N/A if (pCode == NULL) {
0N/A JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
0N/A VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
0N/A return;
0N/A }
784N/A WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL );
0N/A if (isCopy) {
0N/A (*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);
0N/A }
0N/A
0N/A /*
0N/A * Create thread in target process to execute code
0N/A */
0N/A hThread = CreateRemoteThread( hProcess,
0N/A NULL,
0N/A 0,
0N/A (LPTHREAD_START_ROUTINE) pCode,
0N/A pData,
0N/A 0,
0N/A NULL );
0N/A if (hThread != NULL) {
0N/A if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) {
0N/A JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed");
0N/A } else {
0N/A DWORD exitCode;
0N/A GetExitCodeThread(hThread, &exitCode);
0N/A if (exitCode) {
0N/A switch (exitCode) {
0N/A case ERR_OPEN_JVM_FAIL :
0N/A JNU_ThrowIOException(env,
0N/A "jvm.dll not loaded by target process");
0N/A break;
0N/A case ERR_GET_ENQUEUE_FUNC_FAIL :
0N/A JNU_ThrowIOException(env,
0N/A "Unable to enqueue operation: the target VM does not support attach mechanism");
0N/A break;
0N/A default :
0N/A JNU_ThrowInternalError(env,
0N/A "Remote thread failed for unknown reason");
0N/A }
0N/A }
0N/A }
0N/A CloseHandle(hThread);
0N/A } else {
5043N/A if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
5043N/A //
5043N/A // This error will occur when attaching to a process belonging to
5043N/A // another terminal session. See "Remarks":
5043N/A // http://msdn.microsoft.com/en-us/library/ms682437%28VS.85%29.aspx
5043N/A //
5043N/A JNU_ThrowIOException(env,
5043N/A "Insufficient memory or insufficient privileges to attach");
5043N/A } else {
5043N/A JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed");
5043N/A }
0N/A }
0N/A
0N/A VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE);
0N/A VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
0N/A}
0N/A
0N/A/*
0N/A * Attempts to enable the SE_DEBUG_NAME privilege and open the given process.
0N/A */
0N/Astatic HANDLE
0N/AdoPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) {
0N/A HANDLE hToken;
0N/A HANDLE hProcess = NULL;
0N/A LUID luid;
0N/A TOKEN_PRIVILEGES tp, tpPrevious;
0N/A DWORD retLength, error;
0N/A
0N/A /*
0N/A * Get the access token
0N/A */
0N/A if (!OpenThreadToken(GetCurrentThread(),
0N/A TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
0N/A FALSE,
0N/A &hToken)) {
0N/A if (GetLastError() != ERROR_NO_TOKEN) {
0N/A return (HANDLE)NULL;
0N/A }
0N/A
0N/A /*
0N/A * No access token for the thread so impersonate the security context
0N/A * of the process.
0N/A */
0N/A if (!ImpersonateSelf(SecurityImpersonation)) {
0N/A return (HANDLE)NULL;
0N/A }
0N/A if (!OpenThreadToken(GetCurrentThread(),
0N/A TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
0N/A FALSE,
0N/A &hToken)) {
0N/A return (HANDLE)NULL;
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Get LUID for the privilege
0N/A */
0N/A if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
0N/A error = GetLastError();
0N/A CloseHandle(hToken);
0N/A SetLastError(error);
0N/A return (HANDLE)NULL;
0N/A }
0N/A
0N/A /*
0N/A * Enable the privilege
0N/A */
0N/A ZeroMemory(&tp, sizeof(tp));
0N/A tp.PrivilegeCount = 1;
0N/A tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
0N/A tp.Privileges[0].Luid = luid;
0N/A
0N/A error = 0;
0N/A if (AdjustTokenPrivileges(hToken,
0N/A FALSE,
0N/A &tp,
0N/A sizeof(TOKEN_PRIVILEGES),
0N/A &tpPrevious,
0N/A &retLength)) {
0N/A /*
0N/A * If we enabled the privilege then attempt to open the
0N/A * process.
0N/A */
0N/A if (GetLastError() == ERROR_SUCCESS) {
0N/A hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
0N/A if (hProcess == NULL) {
0N/A error = GetLastError();
0N/A }
0N/A } else {
0N/A error = ERROR_ACCESS_DENIED;
0N/A }
0N/A
0N/A /*
0N/A * Revert to the previous privileges
0N/A */
0N/A AdjustTokenPrivileges(hToken,
0N/A FALSE,
0N/A &tpPrevious,
0N/A retLength,
0N/A NULL,
0N/A NULL);
0N/A } else {
0N/A error = GetLastError();
0N/A }
0N/A
0N/A
0N/A /*
0N/A * Close token and restore error
0N/A */
0N/A CloseHandle(hToken);
0N/A SetLastError(error);
0N/A
0N/A return hProcess;
0N/A}
0N/A
0N/A/* convert jstring to C string */
0N/Astatic void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len) {
0N/A jboolean isCopy;
0N/A const char* str;
0N/A
0N/A if (jstr == NULL) {
0N/A cstr[0] = '\0';
0N/A } else {
0N/A str = JNU_GetStringPlatformChars(env, jstr, &isCopy);
0N/A strncpy(cstr, str, len);
0N/A cstr[len-1] = '\0';
0N/A if (isCopy) {
0N/A JNU_ReleaseStringPlatformChars(env, jstr, str);
0N/A }
0N/A }
0N/A}