blob: 94c0e830b8c252dbc48496c36579b29b55ade44e [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25#include <windows.h>
26#include <string.h>
27
28#include "jni.h"
29#include "jni_util.h"
30
31#include "sun_tools_attach_WindowsVirtualMachine.h"
32
33
34/* kernel32 */
35typedef HINSTANCE (WINAPI* LoadLibraryFunc) (LPCTSTR);
36typedef FARPROC (WINAPI* GetProcAddressFunc)(HMODULE, LPCSTR);
37
38/* only on Windows 64-bit or 32-bit application running under WOW64 */
39typedef BOOL (WINAPI *IsWow64ProcessFunc) (HANDLE, PBOOL);
40
41static LoadLibraryFunc _LoadLibrary;
42static GetProcAddressFunc _GetProcAddress;
43static IsWow64ProcessFunc _IsWow64Process;
44
45/* psapi */
46typedef BOOL (WINAPI *EnumProcessModulesFunc) (HANDLE, HMODULE *, DWORD, LPDWORD );
47typedef DWORD (WINAPI *GetModuleFileNameExFunc) ( HANDLE, HMODULE, LPTSTR, DWORD );
48
49/* exported function in target VM */
50typedef 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 */
54static HANDLE
55doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);
56
57/* convert jstring to C string */
58static 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
72typedef struct {
73 LoadLibraryFunc _LoadLibrary;
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/*
91 * Code copied to target process
92 */
93#pragma check_stack (off)
94static DWORD WINAPI thread_func(DataBlock *pData)
95{
96 HINSTANCE h;
97 EnqueueOperationFunc addr;
98
99 h = pData->_LoadLibrary(pData->jvmLib);
100 if (h == NULL) {
101 return ERR_OPEN_JVM_FAIL;
102 }
103
104 addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func1));
105 if (addr == NULL) {
106 addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func2));
107 }
108 if (addr == NULL) {
109 return ERR_GET_ENQUEUE_FUNC_FAIL;
110 }
111
112 /* "null" command - does nothing in the target VM */
113 if (pData->cmd[0] == '\0') {
114 return 0;
115 } else {
116 return (*addr)(pData->cmd, pData->arg[0], pData->arg[1], pData->arg[2], pData->pipename);
117 }
118}
119
120/* This function marks the end of thread_func. */
121static void thread_end (void) {
122}
123#pragma check_stack
124
125
126/*
127 * Class: sun_tools_attach_WindowsVirtualMachine
128 * Method: init
129 * Signature: ()V
130 */
131JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_init
132 (JNIEnv *env, jclass cls)
133{
134 HINSTANCE h = LoadLibrary("kernel32");
135 if (h != NULL) {
136 _LoadLibrary = (LoadLibraryFunc) GetProcAddress(h, "LoadLibraryA");
137 _GetProcAddress = (GetProcAddressFunc)GetProcAddress(h, "GetProcAddress");
138 _IsWow64Process = (IsWow64ProcessFunc)GetProcAddress(h, "IsWow64Process");
139 }
140 if (_LoadLibrary == NULL || _GetProcAddress == NULL) {
141 JNU_ThrowInternalError(env, "Unable to get address of LoadLibraryA or GetProcAddress");
142 }
143}
144
145
146/*
147 * Class: sun_tools_attach_WindowsVirtualMachine
148 * Method: generateStub
149 * Signature: ()[B
150 */
151JNIEXPORT jbyteArray JNICALL Java_sun_tools_attach_WindowsVirtualMachine_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) thread_end - (LPBYTE) thread_func);
161 array= (*env)->NewByteArray(env, (jsize)len);
162 if (array != NULL) {
163 (*env)->SetByteArrayRegion(env, array, 0, (jint)len, (jbyte*)&thread_func);
164 }
165 return array;
166}
167
168/*
169 * Class: sun_tools_attach_WindowsVirtualMachine
170 * Method: openProcess
171 * Signature: (I)J
172 */
173JNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_openProcess
174 (JNIEnv *env, jclass cls, jint pid)
175{
176 HANDLE hProcess;
177
178 /*
179 * Attempt to open process. If it fails then we try to enable the
180 * SE_DEBUG_NAME privilege and retry.
181 */
182 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
183 if (hProcess == NULL && GetLastError() == ERROR_ACCESS_DENIED) {
184 hProcess = doPrivilegedOpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
185 }
186
187 if (hProcess == NULL) {
188 if (GetLastError() == ERROR_INVALID_PARAMETER) {
189 JNU_ThrowIOException(env, "no such process");
190 } else {
191 JNU_ThrowIOExceptionWithLastError(env, "OpenProcess failed");
192 }
193 return (jlong)0;
194 }
195
196 /*
197 * On Windows 64-bit we need to handle 32-bit tools trying to attach to 64-bit
198 * processes (and visa versa). X-architecture attaching is currently not supported
199 * by this implementation.
200 */
201 if (_IsWow64Process != NULL) {
202 BOOL isCurrent32bit, isTarget32bit;
203 (*_IsWow64Process)(GetCurrentProcess(), &isCurrent32bit);
204 (*_IsWow64Process)(hProcess, &isTarget32bit);
205
206 if (isCurrent32bit != isTarget32bit) {
207 CloseHandle(hProcess);
208 #ifdef _WIN64
209 JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",
210 "Unable to attach to 32-bit process running under WOW64");
211 #else
212 JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",
213 "Unable to attach to 64-bit process");
214 #endif
215 }
216 }
217
218 return (jlong)hProcess;
219}
220
221
222/*
223 * Class: sun_tools_attach_WindowsVirtualMachine
224 * Method: closeProcess
225 * Signature: (J)V
226 */
227JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_closeProcess
228 (JNIEnv *env, jclass cls, jlong hProcess)
229{
230 CloseHandle((HANDLE)hProcess);
231}
232
233
234/*
235 * Class: sun_tools_attach_WindowsVirtualMachine
236 * Method: createPipe
237 * Signature: (Ljava/lang/String;)J
238 */
239JNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_createPipe
240 (JNIEnv *env, jclass cls, jstring pipename)
241{
242 HANDLE hPipe;
243 char name[MAX_PIPE_NAME_LENGTH];
244
245 jstring_to_cstring(env, pipename, name, MAX_PIPE_NAME_LENGTH);
246
247 hPipe = CreateNamedPipe(
248 name, // pipe name
249 PIPE_ACCESS_INBOUND, // read access
250 PIPE_TYPE_BYTE | // byte mode
251 PIPE_READMODE_BYTE |
252 PIPE_WAIT, // blocking mode
253 1, // max. instances
254 128, // output buffer size
255 8192, // input buffer size
256 NMPWAIT_USE_DEFAULT_WAIT, // client time-out
257 NULL); // default security attribute
258
259 if (hPipe == INVALID_HANDLE_VALUE) {
260 JNU_ThrowIOExceptionWithLastError(env, "CreateNamedPipe failed");
261 }
262 return (jlong)hPipe;
263}
264
265/*
266 * Class: sun_tools_attach_WindowsVirtualMachine
267 * Method: closePipe
268 * Signature: (J)V
269 */
270JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_closePipe
271 (JNIEnv *env, jclass cls, jlong hPipe)
272{
273 CloseHandle( (HANDLE)hPipe );
274}
275
276/*
277 * Class: sun_tools_attach_WindowsVirtualMachine
278 * Method: connectPipe
279 * Signature: (J)V
280 */
281JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_connectPipe
282 (JNIEnv *env, jclass cls, jlong hPipe)
283{
284 BOOL fConnected;
285
286 fConnected = ConnectNamedPipe((HANDLE)hPipe, NULL) ?
287 TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
288 if (!fConnected) {
289 JNU_ThrowIOExceptionWithLastError(env, "ConnectNamedPipe failed");
290 }
291}
292
293/*
294 * Class: sun_tools_attach_WindowsVirtualMachine
295 * Method: readPipe
296 * Signature: (J[BII)I
297 */
298JNIEXPORT jint JNICALL Java_sun_tools_attach_WindowsVirtualMachine_readPipe
299 (JNIEnv *env, jclass cls, jlong hPipe, jbyteArray ba, jint off, jint baLen)
300{
301 unsigned char buf[128];
302 DWORD len, nread, remaining;
303 BOOL fSuccess;
304
305 len = sizeof(buf);
306 remaining = (DWORD)(baLen - off);
307 if (len > remaining) {
308 len = remaining;
309 }
310
311 fSuccess = ReadFile(
312 (HANDLE)hPipe, // handle to pipe
313 buf, // buffer to receive data
314 len, // size of buffer
315 &nread, // number of bytes read
316 NULL); // not overlapped I/O
317
318 if (!fSuccess) {
319 if (GetLastError() == ERROR_BROKEN_PIPE) {
320 return (jint)-1;
321 } else {
322 JNU_ThrowIOExceptionWithLastError(env, "ReadFile");
323 }
324 } else {
325 if (nread == 0) {
326 return (jint)-1; // EOF
327 } else {
328 (*env)->SetByteArrayRegion(env, ba, off, (jint)nread, (jbyte *)(buf+off));
329 }
330 }
331
332 return (jint)nread;
333}
334
335
336/*
337 * Class: sun_tools_attach_WindowsVirtualMachine
338 * Method: enqueue
339 * Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V
340 */
341JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_enqueue
342 (JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd,
343 jstring pipename, jobjectArray args)
344{
345 DataBlock data;
346 DataBlock* pData;
347 DWORD* pCode;
348 DWORD numBytes;
349 DWORD stubLen;
350 HANDLE hProcess, hThread;
351 jint argsLen, i;
352 jbyte* stubCode;
353 jboolean isCopy;
354
355 /*
356 * Setup data to copy to target process
357 */
358 data._LoadLibrary = _LoadLibrary;
359 data._GetProcAddress = _GetProcAddress;
360
361 strcpy(data.jvmLib, "jvm");
362 strcpy(data.func1, "JVM_EnqueueOperation");
363 strcpy(data.func2, "_JVM_EnqueueOperation@20");
364
365 /*
366 * Command and arguments
367 */
368 jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH);
369 argsLen = (*env)->GetArrayLength(env, args);
370
371 if (argsLen > 0) {
372 if (argsLen > MAX_ARGS) {
373 JNU_ThrowInternalError(env, "Too many arguments");
374 }
375 for (i=0; i<argsLen; i++) {
376 jobject obj = (*env)->GetObjectArrayElement(env, args, i);
377 if (obj == NULL) {
378 data.arg[i][0] = '\0';
379 } else {
380 jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH);
381 }
382 if ((*env)->ExceptionOccurred(env)) return;
383 }
384 }
385 for (i=argsLen; i<MAX_ARGS; i++) {
386 data.arg[i][0] = '\0';
387 }
388
389 /* pipe name */
390 jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH);
391
392 /*
393 * Allocate memory in target process for data and code stub
394 * (assumed aligned and matches architecture of target process)
395 */
396 hProcess = (HANDLE)handle;
397
398 pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE );
399 if (pData == NULL) {
400 JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
401 return;
402 }
403 WriteProcessMemory( hProcess, (LPVOID)pData, (LPVOID)&data, (DWORD)sizeof(DataBlock), &numBytes );
404
405
406 stubLen = (DWORD)(*env)->GetArrayLength(env, stub);
407 stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy);
408
409 pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
410 if (pCode == NULL) {
411 JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");
412 VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
413 return;
414 }
415 WriteProcessMemory( hProcess, (LPVOID)pCode, (LPVOID)stubCode, (DWORD)stubLen, &numBytes );
416 if (isCopy) {
417 (*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);
418 }
419
420 /*
421 * Create thread in target process to execute code
422 */
423 hThread = CreateRemoteThread( hProcess,
424 NULL,
425 0,
426 (LPTHREAD_START_ROUTINE) pCode,
427 pData,
428 0,
429 NULL );
430 if (hThread != NULL) {
431 if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) {
432 JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed");
433 } else {
434 DWORD exitCode;
435 GetExitCodeThread(hThread, &exitCode);
436 if (exitCode) {
437 switch (exitCode) {
438 case ERR_OPEN_JVM_FAIL :
439 JNU_ThrowIOException(env,
440 "jvm.dll not loaded by target process");
441 break;
442 case ERR_GET_ENQUEUE_FUNC_FAIL :
443 JNU_ThrowIOException(env,
444 "Unable to enqueue operation: the target VM does not support attach mechanism");
445 break;
446 default :
447 JNU_ThrowInternalError(env,
448 "Remote thread failed for unknown reason");
449 }
450 }
451 }
452 CloseHandle(hThread);
453 } else {
454 JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed");
455 }
456
457 VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE);
458 VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);
459}
460
461/*
462 * Attempts to enable the SE_DEBUG_NAME privilege and open the given process.
463 */
464static HANDLE
465doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) {
466 HANDLE hToken;
467 HANDLE hProcess = NULL;
468 LUID luid;
469 TOKEN_PRIVILEGES tp, tpPrevious;
470 DWORD retLength, error;
471
472 /*
473 * Get the access token
474 */
475 if (!OpenThreadToken(GetCurrentThread(),
476 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
477 FALSE,
478 &hToken)) {
479 if (GetLastError() != ERROR_NO_TOKEN) {
480 return (HANDLE)NULL;
481 }
482
483 /*
484 * No access token for the thread so impersonate the security context
485 * of the process.
486 */
487 if (!ImpersonateSelf(SecurityImpersonation)) {
488 return (HANDLE)NULL;
489 }
490 if (!OpenThreadToken(GetCurrentThread(),
491 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
492 FALSE,
493 &hToken)) {
494 return (HANDLE)NULL;
495 }
496 }
497
498 /*
499 * Get LUID for the privilege
500 */
501 if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
502 error = GetLastError();
503 CloseHandle(hToken);
504 SetLastError(error);
505 return (HANDLE)NULL;
506 }
507
508 /*
509 * Enable the privilege
510 */
511 ZeroMemory(&tp, sizeof(tp));
512 tp.PrivilegeCount = 1;
513 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
514 tp.Privileges[0].Luid = luid;
515
516 error = 0;
517 if (AdjustTokenPrivileges(hToken,
518 FALSE,
519 &tp,
520 sizeof(TOKEN_PRIVILEGES),
521 &tpPrevious,
522 &retLength)) {
523 /*
524 * If we enabled the privilege then attempt to open the
525 * process.
526 */
527 if (GetLastError() == ERROR_SUCCESS) {
528 hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
529 if (hProcess == NULL) {
530 error = GetLastError();
531 }
532 } else {
533 error = ERROR_ACCESS_DENIED;
534 }
535
536 /*
537 * Revert to the previous privileges
538 */
539 AdjustTokenPrivileges(hToken,
540 FALSE,
541 &tpPrevious,
542 retLength,
543 NULL,
544 NULL);
545 } else {
546 error = GetLastError();
547 }
548
549
550 /*
551 * Close token and restore error
552 */
553 CloseHandle(hToken);
554 SetLastError(error);
555
556 return hProcess;
557}
558
559/* convert jstring to C string */
560static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len) {
561 jboolean isCopy;
562 const char* str;
563
564 if (jstr == NULL) {
565 cstr[0] = '\0';
566 } else {
567 str = JNU_GetStringPlatformChars(env, jstr, &isCopy);
568 strncpy(cstr, str, len);
569 cstr[len-1] = '\0';
570 if (isCopy) {
571 JNU_ReleaseStringPlatformChars(env, jstr, str);
572 }
573 }
574}