1 /*
2 * Copyright (c) 2005, 2018, 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
26 #include "jni_util.h"
27
28 #include <windows.h>
29 #include <Sddl.h>
30 #include <string.h>
31
32 #include "sun_tools_attach_VirtualMachineImpl.h"
33
34 /* kernel32 */
35 typedef HINSTANCE (WINAPI* GetModuleHandleFunc) (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 GetModuleHandleFunc _GetModuleHandle;
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 GetModuleHandleFunc _GetModuleHandle;
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 * Declare library specific JNI_Onload entry if static build
91 */
92 DEF_STATIC_JNI_OnLoad
93
94 /*
95 * Code copied to target process
96 */
97 #pragma check_stack (off)
98 /* Switch off all runtime checks (checks caused by /RTC<x>). They cause the
99 * generated code to contain relative jumps to check functions which make
100 * the code position dependent. */
101 #pragma runtime_checks ("scu", off)
jvm_attach_thread_func(DataBlock * pData)102 DWORD WINAPI jvm_attach_thread_func(DataBlock *pData)
103 {
104 HINSTANCE h;
105 EnqueueOperationFunc addr;
106
107 h = pData->_GetModuleHandle(pData->jvmLib);
108 if (h == NULL) {
109 return ERR_OPEN_JVM_FAIL;
110 }
111
112 addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func1));
113 if (addr == NULL) {
114 addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func2));
115 }
116 if (addr == NULL) {
117 return ERR_GET_ENQUEUE_FUNC_FAIL;
118 }
119
120 /* "null" command - does nothing in the target VM */
121 if (pData->cmd[0] == '\0') {
122 return 0;
123 } else {
124 return (*addr)(pData->cmd, pData->arg[0], pData->arg[1], pData->arg[2], pData->pipename);
125 }
126 }
127
128 /* This function marks the end of jvm_attach_thread_func. */
jvm_attach_thread_func_end(void)129 void jvm_attach_thread_func_end (void) {
130 }
131 #pragma check_stack
132 #pragma runtime_checks ("scu", restore)
133
134 /*
135 * Class: sun_tools_attach_VirtualMachineImpl
136 * Method: init
137 * Signature: ()V
138 */
Java_sun_tools_attach_VirtualMachineImpl_init(JNIEnv * env,jclass cls)139 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_init
140 (JNIEnv *env, jclass cls)
141 {
142 // All following APIs exist on Windows XP with SP2/Windows Server 2008
143 _GetModuleHandle = (GetModuleHandleFunc)GetModuleHandle;
144 _GetProcAddress = (GetProcAddressFunc)GetProcAddress;
145 _IsWow64Process = (IsWow64ProcessFunc)IsWow64Process;
146 }
147
148
149 /*
150 * Class: sun_tools_attach_VirtualMachineImpl
151 * Method: generateStub
152 * Signature: ()[B
153 */
Java_sun_tools_attach_VirtualMachineImpl_generateStub(JNIEnv * env,jclass cls)154 JNIEXPORT jbyteArray JNICALL Java_sun_tools_attach_VirtualMachineImpl_generateStub
155 (JNIEnv *env, jclass cls)
156 {
157 /*
158 * We should replace this with a real stub generator at some point
159 */
160 DWORD len;
161 jbyteArray array;
162
163 len = (DWORD)((LPBYTE) jvm_attach_thread_func_end - (LPBYTE) jvm_attach_thread_func);
164 array= (*env)->NewByteArray(env, (jsize)len);
165 if (array != NULL) {
166 (*env)->SetByteArrayRegion(env, array, 0, (jint)len, (jbyte*)&jvm_attach_thread_func);
167 }
168 return array;
169 }
170
171 /*
172 * Class: sun_tools_attach_VirtualMachineImpl
173 * Method: openProcess
174 * Signature: (I)J
175 */
Java_sun_tools_attach_VirtualMachineImpl_openProcess(JNIEnv * env,jclass cls,jint pid)176 JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_openProcess
177 (JNIEnv *env, jclass cls, jint pid)
178 {
179 HANDLE hProcess = NULL;
180
181 if (pid == (jint) GetCurrentProcessId()) {
182 /* process is attaching to itself; get a pseudo handle instead */
183 hProcess = GetCurrentProcess();
184 /* duplicate the pseudo handle so it can be used in more contexts */
185 if (DuplicateHandle(hProcess, hProcess, hProcess, &hProcess,
186 PROCESS_ALL_ACCESS, FALSE, 0) == 0) {
187 /*
188 * Could not duplicate the handle which isn't a good sign,
189 * but we'll try again with OpenProcess() below.
190 */
191 hProcess = NULL;
192 }
193 }
194
195 if (hProcess == NULL) {
196 /*
197 * Attempt to open process. If it fails then we try to enable the
198 * SE_DEBUG_NAME privilege and retry.
199 */
200 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
201 if (hProcess == NULL && GetLastError() == ERROR_ACCESS_DENIED) {
202 hProcess = doPrivilegedOpenProcess(PROCESS_ALL_ACCESS, FALSE,
203 (DWORD)pid);
204 }
205
206 if (hProcess == NULL) {
207 if (GetLastError() == ERROR_INVALID_PARAMETER) {
208 JNU_ThrowIOException(env, "no such process");
209 } else {
210 char err_mesg[255];
211 /* include the last error in the default detail message */
212 sprintf(err_mesg, "OpenProcess(pid=%d) failed; LastError=0x%x",
213 (int)pid, (int)GetLastError());
214 JNU_ThrowIOExceptionWithLastError(env, err_mesg);
215 }
216 return (jlong)0;
217 }
218 }
219
220 /*
221 * On Windows 64-bit we need to handle 32-bit tools trying to attach to 64-bit
222 * processes (and visa versa). X-architecture attaching is currently not supported
223 * by this implementation.
224 */
225 if (_IsWow64Process != NULL) {
226 BOOL isCurrent32bit, isTarget32bit;
227 (*_IsWow64Process)(GetCurrentProcess(), &isCurrent32bit);
228 (*_IsWow64Process)(hProcess, &isTarget32bit);
229
230 if (isCurrent32bit != isTarget32bit) {
231 CloseHandle(hProcess);
232 #ifdef _WIN64
233 JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",
234 "Unable to attach to 32-bit process running under WOW64");
235 #else
236 JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",
237 "Unable to attach to 64-bit process");
238 #endif
239 }
240 }
241
242 return (jlong)hProcess;
243 }
244
245
246 /*
247 * Class: sun_tools_attach_VirtualMachineImpl
248 * Method: closeProcess
249 * Signature: (J)V
250 */
Java_sun_tools_attach_VirtualMachineImpl_closeProcess(JNIEnv * env,jclass cls,jlong hProcess)251 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_closeProcess
252 (JNIEnv *env, jclass cls, jlong hProcess)
253 {
254 CloseHandle((HANDLE)hProcess);
255 }
256
257
258 /*
259 * Class: sun_tools_attach_VirtualMachineImpl
260 * Method: createPipe
261 * Signature: (Ljava/lang/String;)J
262 */
Java_sun_tools_attach_VirtualMachineImpl_createPipe(JNIEnv * env,jclass cls,jstring pipename)263 JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_createPipe
264 (JNIEnv *env, jclass cls, jstring pipename)
265 {
266 HANDLE hPipe;
267 char name[MAX_PIPE_NAME_LENGTH];
268
269 SECURITY_ATTRIBUTES sa;
270 LPSECURITY_ATTRIBUTES lpSA = NULL;
271 // Custom Security Descriptor is required here to "get" Medium Integrity Level.
272 // In order to allow Medium Integrity Level clients to open
273 // and use a NamedPipe created by an High Integrity Level process.
274 TCHAR *szSD = TEXT("D:") // Discretionary ACL
275 TEXT("(A;OICI;GRGW;;;WD)") // Allow read/write to Everybody
276 TEXT("(A;OICI;GA;;;SY)") // Allow full control to System
277 TEXT("(A;OICI;GA;;;BA)"); // Allow full control to Administrators
278
279 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
280 sa.bInheritHandle = FALSE;
281 sa.lpSecurityDescriptor = NULL;
282
283 if (ConvertStringSecurityDescriptorToSecurityDescriptor
284 (szSD, SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL)) {
285 lpSA = &sa;
286 }
287
288 jstring_to_cstring(env, pipename, name, MAX_PIPE_NAME_LENGTH);
289
290 hPipe = CreateNamedPipe(
291 name, // pipe name
292 PIPE_ACCESS_INBOUND, // read access
293 PIPE_TYPE_BYTE | // byte mode
294 PIPE_READMODE_BYTE |
295 PIPE_WAIT, // blocking mode
296 1, // max. instances
297 128, // output buffer size
298 8192, // input buffer size
299 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
300 lpSA); // security attributes
301
302 LocalFree(sa.lpSecurityDescriptor);
303
304 if (hPipe == INVALID_HANDLE_VALUE) {
305 JNU_ThrowIOExceptionWithLastError(env, "CreateNamedPipe failed");
306 }
307 return (jlong)hPipe;
308 }
309
310 /*
311 * Class: sun_tools_attach_VirtualMachineImpl
312 * Method: closePipe
313 * Signature: (J)V
314 */
Java_sun_tools_attach_VirtualMachineImpl_closePipe(JNIEnv * env,jclass cls,jlong hPipe)315 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_closePipe
316 (JNIEnv *env, jclass cls, jlong hPipe)
317 {
318 CloseHandle((HANDLE)hPipe);
319 }
320
321 /*
322 * Class: sun_tools_attach_VirtualMachineImpl
323 * Method: connectPipe
324 * Signature: (J)V
325 */
Java_sun_tools_attach_VirtualMachineImpl_connectPipe(JNIEnv * env,jclass cls,jlong hPipe)326 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connectPipe
327 (JNIEnv *env, jclass cls, jlong hPipe)
328 {
329 BOOL fConnected;
330
331 fConnected = ConnectNamedPipe((HANDLE)hPipe, NULL) ?
332 TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
333 if (!fConnected) {
334 JNU_ThrowIOExceptionWithLastError(env, "ConnectNamedPipe failed");
335 }
336 }
337
338 /*
339 * Class: sun_tools_attach_VirtualMachineImpl
340 * Method: readPipe
341 * Signature: (J[BII)I
342 */
Java_sun_tools_attach_VirtualMachineImpl_readPipe(JNIEnv * env,jclass cls,jlong hPipe,jbyteArray ba,jint off,jint baLen)343 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_readPipe
344 (JNIEnv *env, jclass cls, jlong hPipe, jbyteArray ba, jint off, jint baLen)
345 {
346 unsigned char buf[128];
347 DWORD len, nread, remaining;
348 BOOL fSuccess;
349
350 len = sizeof(buf);
351 remaining = (DWORD)(baLen - off);
352 if (len > remaining) {
353 len = remaining;
354 }
355
356 fSuccess = ReadFile(
357 (HANDLE)hPipe, // handle to pipe
358 buf, // buffer to receive data
359 len, // size of buffer
360 &nread, // number of bytes read
361 NULL); // not overlapped I/O
362
363 if (!fSuccess) {
364 if (GetLastError() == ERROR_BROKEN_PIPE) {
365 return (jint)-1;
366 } else {
367 JNU_ThrowIOExceptionWithLastError(env, "ReadFile");
368 }
369 } else {
370 if (nread == 0) {
371 return (jint)-1; // EOF
372 } else {
373 (*env)->SetByteArrayRegion(env, ba, off, (jint)nread, (jbyte *)(buf));
374 }
375 }
376
377 return (jint)nread;
378 }
379
380
381 /*
382 * Class: sun_tools_attach_VirtualMachineImpl
383 * Method: enqueue
384 * Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V
385 */
Java_sun_tools_attach_VirtualMachineImpl_enqueue(JNIEnv * env,jclass cls,jlong handle,jbyteArray stub,jstring cmd,jstring pipename,jobjectArray args)386 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue
387 (JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd,
388 jstring pipename, jobjectArray args)
389 {
390 DataBlock data;
391 DataBlock* pData;
392 DWORD* pCode;
393 DWORD stubLen;
394 HANDLE hProcess, hThread;
395 jint argsLen, i;
396 jbyte* stubCode;
397 jboolean isCopy;
398
399 /*
400 * Setup data to copy to target process
401 */
402 data._GetModuleHandle = _GetModuleHandle;
403 data._GetProcAddress = _GetProcAddress;
404
405 strcpy(data.jvmLib, "jvm");
406 strcpy(data.func1, "JVM_EnqueueOperation");
407 strcpy(data.func2, "_JVM_EnqueueOperation@20");
408
409 /*
410 * Command and arguments
411 */
412 jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH);
413 argsLen = (*env)->GetArrayLength(env, args);
414
415 if (argsLen > 0) {
416 if (argsLen > MAX_ARGS) {
417 JNU_ThrowInternalError(env, "Too many arguments");
418 return;
419 }
420 for (i=0; i<argsLen; i++) {
421 jobject obj = (*env)->GetObjectArrayElement(env, args, i);
422 if (obj == NULL) {
423 data.arg[i][0] = '\0';
424 } else {
425 jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH);
426 }
427 if ((*env)->ExceptionOccurred(env)) return;
428 }
429 }
430 for (i = argsLen; i < MAX_ARGS; i++) {
431 data.arg[i][0] = '\0';
432 }
433
434 /* pipe name */
435 jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH);
436
437 /*
438 * Allocate memory in target process for data and code stub
439 * (assumed aligned and matches architecture of target process)
440 */
441 hProcess = (HANDLE)handle;
442
443 pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE );
444 if (pData == NULL) {
445 JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
446 return;
447 }
448 WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL );
449
450
451 stubLen = (DWORD)(*env)->GetArrayLength(env, stub);
452 stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy);
453
454 if ((*env)->ExceptionOccurred(env)) return;
455
456 pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
457 if (pCode == NULL) {
458 JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
459 VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
460 return;
461 }
462 WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL );
463 if (isCopy) {
464 (*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);
465 }
466
467 /*
468 * Create thread in target process to execute code
469 */
470 hThread = CreateRemoteThread( hProcess,
471 NULL,
472 0,
473 (LPTHREAD_START_ROUTINE) pCode,
474 pData,
475 0,
476 NULL );
477 if (hThread != NULL) {
478 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) {
479 JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed");
480 } else {
481 DWORD exitCode;
482 GetExitCodeThread(hThread, &exitCode);
483 if (exitCode) {
484 switch (exitCode) {
485 case ERR_OPEN_JVM_FAIL :
486 JNU_ThrowIOException(env,
487 "jvm.dll not loaded by target process");
488 break;
489 case ERR_GET_ENQUEUE_FUNC_FAIL :
490 JNU_ThrowIOException(env,
491 "Unable to enqueue operation: the target VM does not support attach mechanism");
492 break;
493 default : {
494 char errmsg[128];
495 sprintf(errmsg, "Remote thread failed for unknown reason (%d)", exitCode);
496 JNU_ThrowInternalError(env, errmsg);
497 }
498 }
499 }
500 }
501 CloseHandle(hThread);
502 } else {
503 if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
504 //
505 // This error will occur when attaching to a process belonging to
506 // another terminal session. See "Remarks":
507 // http://msdn.microsoft.com/en-us/library/ms682437%28VS.85%29.aspx
508 //
509 JNU_ThrowIOException(env,
510 "Insufficient memory or insufficient privileges to attach");
511 } else {
512 JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed");
513 }
514 }
515
516 VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE);
517 VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
518 }
519
520 /*
521 * Attempts to enable the SE_DEBUG_NAME privilege and open the given process.
522 */
523 static HANDLE
doPrivilegedOpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwProcessId)524 doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) {
525 HANDLE hToken;
526 HANDLE hProcess = NULL;
527 LUID luid;
528 TOKEN_PRIVILEGES tp, tpPrevious;
529 DWORD retLength, error;
530
531 /*
532 * Get the access token
533 */
534 if (!OpenThreadToken(GetCurrentThread(),
535 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
536 FALSE,
537 &hToken)) {
538 if (GetLastError() != ERROR_NO_TOKEN) {
539 return (HANDLE)NULL;
540 }
541
542 /*
543 * No access token for the thread so impersonate the security context
544 * of the process.
545 */
546 if (!ImpersonateSelf(SecurityImpersonation)) {
547 return (HANDLE)NULL;
548 }
549 if (!OpenThreadToken(GetCurrentThread(),
550 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
551 FALSE,
552 &hToken)) {
553 return (HANDLE)NULL;
554 }
555 }
556
557 /*
558 * Get LUID for the privilege
559 */
560 if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
561 error = GetLastError();
562 CloseHandle(hToken);
563 SetLastError(error);
564 return (HANDLE)NULL;
565 }
566
567 /*
568 * Enable the privilege
569 */
570 ZeroMemory(&tp, sizeof(tp));
571 tp.PrivilegeCount = 1;
572 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
573 tp.Privileges[0].Luid = luid;
574
575 error = 0;
576 if (AdjustTokenPrivileges(hToken,
577 FALSE,
578 &tp,
579 sizeof(TOKEN_PRIVILEGES),
580 &tpPrevious,
581 &retLength)) {
582 /*
583 * If we enabled the privilege then attempt to open the
584 * process.
585 */
586 if (GetLastError() == ERROR_SUCCESS) {
587 hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
588 if (hProcess == NULL) {
589 error = GetLastError();
590 }
591 } else {
592 error = ERROR_ACCESS_DENIED;
593 }
594
595 /*
596 * Revert to the previous privileges
597 */
598 AdjustTokenPrivileges(hToken,
599 FALSE,
600 &tpPrevious,
601 retLength,
602 NULL,
603 NULL);
604 } else {
605 error = GetLastError();
606 }
607
608
609 /*
610 * Close token and restore error
611 */
612 CloseHandle(hToken);
613 SetLastError(error);
614
615 return hProcess;
616 }
617
618 /* convert jstring to C string */
jstring_to_cstring(JNIEnv * env,jstring jstr,char * cstr,int len)619 static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len) {
620 jboolean isCopy;
621 const char* str;
622
623 if (jstr == NULL) {
624 cstr[0] = '\0';
625 } else {
626 str = JNU_GetStringPlatformChars(env, jstr, &isCopy);
627 if ((*env)->ExceptionOccurred(env)) return;
628
629 strncpy(cstr, str, len);
630 cstr[len-1] = '\0';
631 if (isCopy) {
632 JNU_ReleaseStringPlatformChars(env, jstr, str);
633 }
634 }
635 }
636