Path: blob/master/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c
40932 views
/*1* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include "jni_util.h"2627#include <windows.h>28#include <Sddl.h>29#include <string.h>3031#include "sun_tools_attach_VirtualMachineImpl.h"3233/* kernel32 */34typedef HINSTANCE (WINAPI* GetModuleHandleFunc) (LPCTSTR);35typedef FARPROC (WINAPI* GetProcAddressFunc)(HMODULE, LPCSTR);3637/* only on Windows 64-bit or 32-bit application running under WOW64 */38typedef BOOL (WINAPI *IsWow64ProcessFunc) (HANDLE, PBOOL);3940static GetModuleHandleFunc _GetModuleHandle;41static GetProcAddressFunc _GetProcAddress;42static IsWow64ProcessFunc _IsWow64Process;4344/* psapi */45typedef BOOL (WINAPI *EnumProcessModulesFunc) (HANDLE, HMODULE *, DWORD, LPDWORD );46typedef DWORD (WINAPI *GetModuleFileNameExFunc) ( HANDLE, HMODULE, LPTSTR, DWORD );4748/* exported function in target VM */49typedef jint (WINAPI* EnqueueOperationFunc)50(const char* cmd, const char* arg1, const char* arg2, const char* arg3, const char* pipename);5152/* OpenProcess with SE_DEBUG_NAME privilege */53static HANDLE54doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);5556/* convert jstring to C string */57static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len);585960/*61* Data copied to target process62*/6364#define MAX_LIBNAME_LENGTH 1665#define MAX_FUNC_LENGTH 3266#define MAX_CMD_LENGTH 1667#define MAX_ARG_LENGTH 102468#define MAX_ARGS 369#define MAX_PIPE_NAME_LENGTH 2567071typedef struct {72GetModuleHandleFunc _GetModuleHandle;73GetProcAddressFunc _GetProcAddress;74char jvmLib[MAX_LIBNAME_LENGTH]; /* "jvm.dll" */75char func1[MAX_FUNC_LENGTH];76char func2[MAX_FUNC_LENGTH];77char cmd[MAX_CMD_LENGTH]; /* "load", "dump", ... */78char arg[MAX_ARGS][MAX_ARG_LENGTH]; /* arguments to command */79char pipename[MAX_PIPE_NAME_LENGTH];80} DataBlock;8182/*83* Return codes from enqueue function executed in target VM84*/85#define ERR_OPEN_JVM_FAIL 20086#define ERR_GET_ENQUEUE_FUNC_FAIL 2018788/*89* Declare library specific JNI_Onload entry if static build90*/91DEF_STATIC_JNI_OnLoad9293/*94* Code copied to target process95*/96#pragma check_stack (off)97/* Switch off all runtime checks (checks caused by /RTC<x>). They cause the98* generated code to contain relative jumps to check functions which make99* the code position dependent. */100#pragma runtime_checks ("scu", off)101DWORD WINAPI jvm_attach_thread_func(DataBlock *pData)102{103HINSTANCE h;104EnqueueOperationFunc addr;105106h = pData->_GetModuleHandle(pData->jvmLib);107if (h == NULL) {108return ERR_OPEN_JVM_FAIL;109}110111addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func1));112if (addr == NULL) {113addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func2));114}115if (addr == NULL) {116return ERR_GET_ENQUEUE_FUNC_FAIL;117}118119/* "null" command - does nothing in the target VM */120if (pData->cmd[0] == '\0') {121return 0;122} else {123return (*addr)(pData->cmd, pData->arg[0], pData->arg[1], pData->arg[2], pData->pipename);124}125}126127/* This function marks the end of jvm_attach_thread_func. */128void jvm_attach_thread_func_end (void) {129}130#pragma check_stack131#pragma runtime_checks ("scu", restore)132133/*134* Class: sun_tools_attach_VirtualMachineImpl135* Method: init136* Signature: ()V137*/138JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_init139(JNIEnv *env, jclass cls)140{141// All following APIs exist on Windows XP with SP2/Windows Server 2008142_GetModuleHandle = (GetModuleHandleFunc)GetModuleHandle;143_GetProcAddress = (GetProcAddressFunc)GetProcAddress;144_IsWow64Process = (IsWow64ProcessFunc)IsWow64Process;145}146147148/*149* Class: sun_tools_attach_VirtualMachineImpl150* Method: generateStub151* Signature: ()[B152*/153JNIEXPORT jbyteArray JNICALL Java_sun_tools_attach_VirtualMachineImpl_generateStub154(JNIEnv *env, jclass cls)155{156/*157* We should replace this with a real stub generator at some point158*/159DWORD len;160jbyteArray array;161162len = (DWORD)((LPBYTE) jvm_attach_thread_func_end - (LPBYTE) jvm_attach_thread_func);163array= (*env)->NewByteArray(env, (jsize)len);164if (array != NULL) {165(*env)->SetByteArrayRegion(env, array, 0, (jint)len, (jbyte*)&jvm_attach_thread_func);166}167return array;168}169170/*171* Class: sun_tools_attach_VirtualMachineImpl172* Method: openProcess173* Signature: (I)J174*/175JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_openProcess176(JNIEnv *env, jclass cls, jint pid)177{178HANDLE hProcess = NULL;179180if (pid == (jint) GetCurrentProcessId()) {181/* process is attaching to itself; get a pseudo handle instead */182hProcess = GetCurrentProcess();183/* duplicate the pseudo handle so it can be used in more contexts */184if (DuplicateHandle(hProcess, hProcess, hProcess, &hProcess,185PROCESS_ALL_ACCESS, FALSE, 0) == 0) {186/*187* Could not duplicate the handle which isn't a good sign,188* but we'll try again with OpenProcess() below.189*/190hProcess = NULL;191}192}193194if (hProcess == NULL) {195/*196* Attempt to open process. If it fails then we try to enable the197* SE_DEBUG_NAME privilege and retry.198*/199hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);200if (hProcess == NULL && GetLastError() == ERROR_ACCESS_DENIED) {201hProcess = doPrivilegedOpenProcess(PROCESS_ALL_ACCESS, FALSE,202(DWORD)pid);203}204205if (hProcess == NULL) {206if (GetLastError() == ERROR_INVALID_PARAMETER) {207JNU_ThrowIOException(env, "no such process");208} else {209char err_mesg[255];210/* include the last error in the default detail message */211sprintf(err_mesg, "OpenProcess(pid=%d) failed; LastError=0x%x",212(int)pid, (int)GetLastError());213JNU_ThrowIOExceptionWithLastError(env, err_mesg);214}215return (jlong)0;216}217}218219/*220* On Windows 64-bit we need to handle 32-bit tools trying to attach to 64-bit221* processes (and visa versa). X-architecture attaching is currently not supported222* by this implementation.223*/224if (_IsWow64Process != NULL) {225BOOL isCurrent32bit, isTarget32bit;226(*_IsWow64Process)(GetCurrentProcess(), &isCurrent32bit);227(*_IsWow64Process)(hProcess, &isTarget32bit);228229if (isCurrent32bit != isTarget32bit) {230CloseHandle(hProcess);231#ifdef _WIN64232JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",233"Unable to attach to 32-bit process running under WOW64");234#else235JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",236"Unable to attach to 64-bit process");237#endif238}239}240241return (jlong)hProcess;242}243244245/*246* Class: sun_tools_attach_VirtualMachineImpl247* Method: closeProcess248* Signature: (J)V249*/250JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_closeProcess251(JNIEnv *env, jclass cls, jlong hProcess)252{253CloseHandle((HANDLE)hProcess);254}255256257/*258* Class: sun_tools_attach_VirtualMachineImpl259* Method: createPipe260* Signature: (Ljava/lang/String;)J261*/262JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_createPipe263(JNIEnv *env, jclass cls, jstring pipename)264{265HANDLE hPipe;266char name[MAX_PIPE_NAME_LENGTH];267268SECURITY_ATTRIBUTES sa;269LPSECURITY_ATTRIBUTES lpSA = NULL;270// Custom Security Descriptor is required here to "get" Medium Integrity Level.271// In order to allow Medium Integrity Level clients to open272// and use a NamedPipe created by an High Integrity Level process.273TCHAR *szSD = TEXT("D:") // Discretionary ACL274TEXT("(A;OICI;GRGW;;;WD)") // Allow read/write to Everybody275TEXT("(A;OICI;GA;;;SY)") // Allow full control to System276TEXT("(A;OICI;GA;;;BA)"); // Allow full control to Administrators277278sa.nLength = sizeof(SECURITY_ATTRIBUTES);279sa.bInheritHandle = FALSE;280sa.lpSecurityDescriptor = NULL;281282if (ConvertStringSecurityDescriptorToSecurityDescriptor283(szSD, SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL)) {284lpSA = &sa;285}286287jstring_to_cstring(env, pipename, name, MAX_PIPE_NAME_LENGTH);288289hPipe = CreateNamedPipe(290name, // pipe name291PIPE_ACCESS_INBOUND, // read access292PIPE_TYPE_BYTE | // byte mode293PIPE_READMODE_BYTE |294PIPE_WAIT, // blocking mode2951, // max. instances296128, // output buffer size2978192, // input buffer size298NMPWAIT_USE_DEFAULT_WAIT, // client time-out299lpSA); // security attributes300301LocalFree(sa.lpSecurityDescriptor);302303if (hPipe == INVALID_HANDLE_VALUE) {304JNU_ThrowIOExceptionWithLastError(env, "CreateNamedPipe failed");305}306return (jlong)hPipe;307}308309/*310* Class: sun_tools_attach_VirtualMachineImpl311* Method: closePipe312* Signature: (J)V313*/314JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_closePipe315(JNIEnv *env, jclass cls, jlong hPipe)316{317CloseHandle((HANDLE)hPipe);318}319320/*321* Class: sun_tools_attach_VirtualMachineImpl322* Method: connectPipe323* Signature: (J)V324*/325JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connectPipe326(JNIEnv *env, jclass cls, jlong hPipe)327{328BOOL fConnected;329330fConnected = ConnectNamedPipe((HANDLE)hPipe, NULL) ?331TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);332if (!fConnected) {333JNU_ThrowIOExceptionWithLastError(env, "ConnectNamedPipe failed");334}335}336337/*338* Class: sun_tools_attach_VirtualMachineImpl339* Method: readPipe340* Signature: (J[BII)I341*/342JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_readPipe343(JNIEnv *env, jclass cls, jlong hPipe, jbyteArray ba, jint off, jint baLen)344{345unsigned char buf[128];346DWORD len, nread, remaining;347BOOL fSuccess;348349len = sizeof(buf);350remaining = (DWORD)(baLen - off);351if (len > remaining) {352len = remaining;353}354355fSuccess = ReadFile(356(HANDLE)hPipe, // handle to pipe357buf, // buffer to receive data358len, // size of buffer359&nread, // number of bytes read360NULL); // not overlapped I/O361362if (!fSuccess) {363if (GetLastError() == ERROR_BROKEN_PIPE) {364return (jint)-1;365} else {366JNU_ThrowIOExceptionWithLastError(env, "ReadFile");367}368} else {369if (nread == 0) {370return (jint)-1; // EOF371} else {372(*env)->SetByteArrayRegion(env, ba, off, (jint)nread, (jbyte *)(buf));373}374}375376return (jint)nread;377}378379380/*381* Class: sun_tools_attach_VirtualMachineImpl382* Method: enqueue383* Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V384*/385JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue386(JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd,387jstring pipename, jobjectArray args)388{389DataBlock data;390DataBlock* pData;391DWORD* pCode;392DWORD stubLen;393HANDLE hProcess, hThread;394jint argsLen, i;395jbyte* stubCode;396jboolean isCopy;397398/*399* Setup data to copy to target process400*/401data._GetModuleHandle = _GetModuleHandle;402data._GetProcAddress = _GetProcAddress;403404strcpy(data.jvmLib, "jvm");405strcpy(data.func1, "JVM_EnqueueOperation");406strcpy(data.func2, "_JVM_EnqueueOperation@20");407408/*409* Command and arguments410*/411jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH);412argsLen = (*env)->GetArrayLength(env, args);413414if (argsLen > 0) {415if (argsLen > MAX_ARGS) {416JNU_ThrowInternalError(env, "Too many arguments");417return;418}419for (i=0; i<argsLen; i++) {420jobject obj = (*env)->GetObjectArrayElement(env, args, i);421if (obj == NULL) {422data.arg[i][0] = '\0';423} else {424jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH);425}426if ((*env)->ExceptionOccurred(env)) return;427}428}429for (i = argsLen; i < MAX_ARGS; i++) {430data.arg[i][0] = '\0';431}432433/* pipe name */434jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH);435436/*437* Allocate memory in target process for data and code stub438* (assumed aligned and matches architecture of target process)439*/440hProcess = (HANDLE)handle;441442pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE );443if (pData == NULL) {444JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");445return;446}447WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL );448449450stubLen = (DWORD)(*env)->GetArrayLength(env, stub);451stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy);452453if ((*env)->ExceptionOccurred(env)) return;454455pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE );456if (pCode == NULL) {457JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");458VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);459(*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);460return;461}462WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL );463(*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);464465/*466* Create thread in target process to execute code467*/468hThread = CreateRemoteThread( hProcess,469NULL,4700,471(LPTHREAD_START_ROUTINE) pCode,472pData,4730,474NULL );475if (hThread != NULL) {476if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) {477JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed");478} else {479DWORD exitCode;480GetExitCodeThread(hThread, &exitCode);481if (exitCode) {482switch (exitCode) {483case ERR_OPEN_JVM_FAIL :484JNU_ThrowIOException(env,485"jvm.dll not loaded by target process");486break;487case ERR_GET_ENQUEUE_FUNC_FAIL :488JNU_ThrowIOException(env,489"Unable to enqueue operation: the target VM does not support attach mechanism");490break;491default : {492char errmsg[128];493sprintf(errmsg, "Remote thread failed for unknown reason (%d)", exitCode);494JNU_ThrowInternalError(env, errmsg);495}496}497}498}499CloseHandle(hThread);500} else {501if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {502//503// This error will occur when attaching to a process belonging to504// another terminal session. See "Remarks":505// http://msdn.microsoft.com/en-us/library/ms682437%28VS.85%29.aspx506//507JNU_ThrowIOException(env,508"Insufficient memory or insufficient privileges to attach");509} else {510JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed");511}512}513514VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE);515VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);516}517518/*519* Attempts to enable the SE_DEBUG_NAME privilege and open the given process.520*/521static HANDLE522doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) {523HANDLE hToken;524HANDLE hProcess = NULL;525LUID luid;526TOKEN_PRIVILEGES tp, tpPrevious;527DWORD retLength, error;528529/*530* Get the access token531*/532if (!OpenThreadToken(GetCurrentThread(),533TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,534FALSE,535&hToken)) {536if (GetLastError() != ERROR_NO_TOKEN) {537return (HANDLE)NULL;538}539540/*541* No access token for the thread so impersonate the security context542* of the process.543*/544if (!ImpersonateSelf(SecurityImpersonation)) {545return (HANDLE)NULL;546}547if (!OpenThreadToken(GetCurrentThread(),548TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,549FALSE,550&hToken)) {551return (HANDLE)NULL;552}553}554555/*556* Get LUID for the privilege557*/558if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {559error = GetLastError();560CloseHandle(hToken);561SetLastError(error);562return (HANDLE)NULL;563}564565/*566* Enable the privilege567*/568ZeroMemory(&tp, sizeof(tp));569tp.PrivilegeCount = 1;570tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;571tp.Privileges[0].Luid = luid;572573error = 0;574if (AdjustTokenPrivileges(hToken,575FALSE,576&tp,577sizeof(TOKEN_PRIVILEGES),578&tpPrevious,579&retLength)) {580/*581* If we enabled the privilege then attempt to open the582* process.583*/584if (GetLastError() == ERROR_SUCCESS) {585hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);586if (hProcess == NULL) {587error = GetLastError();588}589} else {590error = ERROR_ACCESS_DENIED;591}592593/*594* Revert to the previous privileges595*/596AdjustTokenPrivileges(hToken,597FALSE,598&tpPrevious,599retLength,600NULL,601NULL);602} else {603error = GetLastError();604}605606607/*608* Close token and restore error609*/610CloseHandle(hToken);611SetLastError(error);612613return hProcess;614}615616/* convert jstring to C string */617static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len) {618jboolean isCopy;619const char* str;620621if (jstr == NULL) {622cstr[0] = '\0';623} else {624str = JNU_GetStringPlatformChars(env, jstr, &isCopy);625if ((*env)->ExceptionOccurred(env)) return;626627strncpy(cstr, str, len);628cstr[len-1] = '\0';629if (isCopy) {630JNU_ReleaseStringPlatformChars(env, jstr, str);631}632}633}634635636