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