Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c
67682 views
1
/*
2
* Copyright (c) 2005, 2021, 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)
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. */
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
memset(&data, 0, sizeof(data));
403
data._GetModuleHandle = _GetModuleHandle;
404
data._GetProcAddress = _GetProcAddress;
405
406
strcpy(data.jvmLib, "jvm");
407
strcpy(data.func1, "JVM_EnqueueOperation");
408
strcpy(data.func2, "_JVM_EnqueueOperation@20");
409
410
/*
411
* Command and arguments
412
*/
413
jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH);
414
argsLen = (*env)->GetArrayLength(env, args);
415
416
if (argsLen > 0) {
417
if (argsLen > MAX_ARGS) {
418
JNU_ThrowInternalError(env, "Too many arguments");
419
return;
420
}
421
for (i=0; i<argsLen; i++) {
422
jobject obj = (*env)->GetObjectArrayElement(env, args, i);
423
if (obj == NULL) {
424
data.arg[i][0] = '\0';
425
} else {
426
jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH);
427
}
428
if ((*env)->ExceptionOccurred(env)) return;
429
}
430
}
431
for (i = argsLen; i < MAX_ARGS; i++) {
432
data.arg[i][0] = '\0';
433
}
434
435
/* pipe name */
436
jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH);
437
438
/*
439
* Allocate memory in target process for data and code stub
440
* (assumed aligned and matches architecture of target process)
441
*/
442
hProcess = (HANDLE)handle;
443
444
pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE );
445
if (pData == NULL) {
446
JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
447
return;
448
}
449
WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL );
450
451
452
stubLen = (DWORD)(*env)->GetArrayLength(env, stub);
453
stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy);
454
455
if ((*env)->ExceptionOccurred(env)) return;
456
457
pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
458
if (pCode == NULL) {
459
JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
460
VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
461
(*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);
462
return;
463
}
464
WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL );
465
(*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);
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
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 */
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
637