Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/native/sun/tools/attach/WindowsVirtualMachine.c
32288 views
/*1* Copyright (c) 2005, 2013, 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*/24#include <windows.h>25#include <Sddl.h>26#include <string.h>2728#include "jni.h"29#include "jni_util.h"3031#include "sun_tools_attach_WindowsVirtualMachine.h"323334/* kernel32 */35typedef HINSTANCE (WINAPI* GetModuleHandleFunc) (LPCTSTR);36typedef FARPROC (WINAPI* GetProcAddressFunc)(HMODULE, LPCSTR);3738/* only on Windows 64-bit or 32-bit application running under WOW64 */39typedef BOOL (WINAPI *IsWow64ProcessFunc) (HANDLE, PBOOL);4041static GetModuleHandleFunc _GetModuleHandle;42static GetProcAddressFunc _GetProcAddress;43static IsWow64ProcessFunc _IsWow64Process;4445/* psapi */46typedef BOOL (WINAPI *EnumProcessModulesFunc) (HANDLE, HMODULE *, DWORD, LPDWORD );47typedef DWORD (WINAPI *GetModuleFileNameExFunc) ( HANDLE, HMODULE, LPTSTR, DWORD );4849/* 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);5253/* OpenProcess with SE_DEBUG_NAME privilege */54static HANDLE55doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);5657/* convert jstring to C string */58static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len);596061/*62* Data copied to target process63*/6465#define MAX_LIBNAME_LENGTH 1666#define MAX_FUNC_LENGTH 3267#define MAX_CMD_LENGTH 1668#define MAX_ARG_LENGTH 102469#define MAX_ARGS 370#define MAX_PIPE_NAME_LENGTH 2567172typedef struct {73GetModuleHandleFunc _GetModuleHandle;74GetProcAddressFunc _GetProcAddress;75char jvmLib[MAX_LIBNAME_LENGTH]; /* "jvm.dll" */76char func1[MAX_FUNC_LENGTH];77char func2[MAX_FUNC_LENGTH];78char cmd[MAX_CMD_LENGTH]; /* "load", "dump", ... */79char arg[MAX_ARGS][MAX_ARG_LENGTH]; /* arguments to command */80char pipename[MAX_PIPE_NAME_LENGTH];81} DataBlock;8283/*84* Return codes from enqueue function executed in target VM85*/86#define ERR_OPEN_JVM_FAIL 20087#define ERR_GET_ENQUEUE_FUNC_FAIL 201888990/*91* Code copied to target process92*/93#pragma check_stack (off)94DWORD WINAPI jvm_attach_thread_func(DataBlock *pData)95{96HINSTANCE h;97EnqueueOperationFunc addr;9899h = pData->_GetModuleHandle(pData->jvmLib);100if (h == NULL) {101return ERR_OPEN_JVM_FAIL;102}103104addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func1));105if (addr == NULL) {106addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func2));107}108if (addr == NULL) {109return ERR_GET_ENQUEUE_FUNC_FAIL;110}111112/* "null" command - does nothing in the target VM */113if (pData->cmd[0] == '\0') {114return 0;115} else {116return (*addr)(pData->cmd, pData->arg[0], pData->arg[1], pData->arg[2], pData->pipename);117}118}119120/* This function marks the end of jvm_attach_thread_func. */121void jvm_attach_thread_func_end (void) {122}123#pragma check_stack124125126/*127* Class: sun_tools_attach_WindowsVirtualMachine128* Method: init129* Signature: ()V130*/131JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_init132(JNIEnv *env, jclass cls)133{134// All following APIs exist on Windows XP with SP2/Windows Server 2008135_GetModuleHandle = (GetModuleHandleFunc)GetModuleHandle;136_GetProcAddress = (GetProcAddressFunc)GetProcAddress;137_IsWow64Process = (IsWow64ProcessFunc)IsWow64Process;138}139140141/*142* Class: sun_tools_attach_WindowsVirtualMachine143* Method: generateStub144* Signature: ()[B145*/146JNIEXPORT jbyteArray JNICALL Java_sun_tools_attach_WindowsVirtualMachine_generateStub147(JNIEnv *env, jclass cls)148{149/*150* We should replace this with a real stub generator at some point151*/152DWORD len;153jbyteArray array;154155len = (DWORD)((LPBYTE) jvm_attach_thread_func_end - (LPBYTE) jvm_attach_thread_func);156array= (*env)->NewByteArray(env, (jsize)len);157if (array != NULL) {158(*env)->SetByteArrayRegion(env, array, 0, (jint)len, (jbyte*)&jvm_attach_thread_func);159}160return array;161}162163/*164* Class: sun_tools_attach_WindowsVirtualMachine165* Method: openProcess166* Signature: (I)J167*/168JNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_openProcess169(JNIEnv *env, jclass cls, jint pid)170{171HANDLE hProcess = NULL;172173if (pid == (jint) GetCurrentProcessId()) {174/* process is attaching to itself; get a pseudo handle instead */175hProcess = GetCurrentProcess();176/* duplicate the pseudo handle so it can be used in more contexts */177if (DuplicateHandle(hProcess, hProcess, hProcess, &hProcess,178PROCESS_ALL_ACCESS, FALSE, 0) == 0) {179/*180* Could not duplicate the handle which isn't a good sign,181* but we'll try again with OpenProcess() below.182*/183hProcess = NULL;184}185}186187if (hProcess == NULL) {188/*189* Attempt to open process. If it fails then we try to enable the190* SE_DEBUG_NAME privilege and retry.191*/192hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);193if (hProcess == NULL && GetLastError() == ERROR_ACCESS_DENIED) {194hProcess = doPrivilegedOpenProcess(PROCESS_ALL_ACCESS, FALSE,195(DWORD)pid);196}197198if (hProcess == NULL) {199if (GetLastError() == ERROR_INVALID_PARAMETER) {200JNU_ThrowIOException(env, "no such process");201} else {202char err_mesg[255];203/* include the last error in the default detail message */204sprintf(err_mesg, "OpenProcess(pid=%d) failed; LastError=0x%x",205(int)pid, (int)GetLastError());206JNU_ThrowIOExceptionWithLastError(env, err_mesg);207}208return (jlong)0;209}210}211212/*213* On Windows 64-bit we need to handle 32-bit tools trying to attach to 64-bit214* processes (and visa versa). X-architecture attaching is currently not supported215* by this implementation.216*/217if (_IsWow64Process != NULL) {218BOOL isCurrent32bit, isTarget32bit;219(*_IsWow64Process)(GetCurrentProcess(), &isCurrent32bit);220(*_IsWow64Process)(hProcess, &isTarget32bit);221222if (isCurrent32bit != isTarget32bit) {223CloseHandle(hProcess);224#ifdef _WIN64225JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",226"Unable to attach to 32-bit process running under WOW64");227#else228JNU_ThrowByName(env, "com/sun/tools/attach/AttachNotSupportedException",229"Unable to attach to 64-bit process");230#endif231}232}233234return (jlong)hProcess;235}236237238/*239* Class: sun_tools_attach_WindowsVirtualMachine240* Method: closeProcess241* Signature: (J)V242*/243JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_closeProcess244(JNIEnv *env, jclass cls, jlong hProcess)245{246CloseHandle((HANDLE)hProcess);247}248249250/*251* Class: sun_tools_attach_WindowsVirtualMachine252* Method: createPipe253* Signature: (Ljava/lang/String;)J254*/255JNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_createPipe256(JNIEnv *env, jclass cls, jstring pipename)257{258HANDLE hPipe;259char name[MAX_PIPE_NAME_LENGTH];260261SECURITY_ATTRIBUTES sa;262LPSECURITY_ATTRIBUTES lpSA = NULL;263// Custom Security Descriptor is required here to "get" Medium Integrity Level.264// In order to allow Medium Integrity Level clients to open265// and use a NamedPipe created by an High Integrity Level process.266TCHAR *szSD = TEXT("D:") // Discretionary ACL267TEXT("(A;OICI;GRGW;;;WD)") // Allow read/write to Everybody268TEXT("(A;OICI;GA;;;SY)") // Allow full control to System269TEXT("(A;OICI;GA;;;BA)"); // Allow full control to Administrators270271sa.nLength = sizeof(SECURITY_ATTRIBUTES);272sa.bInheritHandle = FALSE;273sa.lpSecurityDescriptor = NULL;274275if (ConvertStringSecurityDescriptorToSecurityDescriptor276(szSD, SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL)) {277lpSA = &sa;278}279280jstring_to_cstring(env, pipename, name, MAX_PIPE_NAME_LENGTH);281282hPipe = CreateNamedPipe(283name, // pipe name284PIPE_ACCESS_INBOUND, // read access285PIPE_TYPE_BYTE | // byte mode286PIPE_READMODE_BYTE |287PIPE_WAIT, // blocking mode2881, // max. instances289128, // output buffer size2908192, // input buffer size291NMPWAIT_USE_DEFAULT_WAIT, // client time-out292lpSA); // security attributes293294LocalFree(sa.lpSecurityDescriptor);295296if (hPipe == INVALID_HANDLE_VALUE) {297JNU_ThrowIOExceptionWithLastError(env, "CreateNamedPipe failed");298}299return (jlong)hPipe;300}301302/*303* Class: sun_tools_attach_WindowsVirtualMachine304* Method: closePipe305* Signature: (J)V306*/307JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_closePipe308(JNIEnv *env, jclass cls, jlong hPipe)309{310CloseHandle( (HANDLE)hPipe );311}312313/*314* Class: sun_tools_attach_WindowsVirtualMachine315* Method: connectPipe316* Signature: (J)V317*/318JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_connectPipe319(JNIEnv *env, jclass cls, jlong hPipe)320{321BOOL fConnected;322323fConnected = ConnectNamedPipe((HANDLE)hPipe, NULL) ?324TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);325if (!fConnected) {326JNU_ThrowIOExceptionWithLastError(env, "ConnectNamedPipe failed");327}328}329330/*331* Class: sun_tools_attach_WindowsVirtualMachine332* Method: readPipe333* Signature: (J[BII)I334*/335JNIEXPORT jint JNICALL Java_sun_tools_attach_WindowsVirtualMachine_readPipe336(JNIEnv *env, jclass cls, jlong hPipe, jbyteArray ba, jint off, jint baLen)337{338unsigned char buf[128];339DWORD len, nread, remaining;340BOOL fSuccess;341342len = sizeof(buf);343remaining = (DWORD)(baLen - off);344if (len > remaining) {345len = remaining;346}347348fSuccess = ReadFile(349(HANDLE)hPipe, // handle to pipe350buf, // buffer to receive data351len, // size of buffer352&nread, // number of bytes read353NULL); // not overlapped I/O354355if (!fSuccess) {356if (GetLastError() == ERROR_BROKEN_PIPE) {357return (jint)-1;358} else {359JNU_ThrowIOExceptionWithLastError(env, "ReadFile");360}361} else {362if (nread == 0) {363return (jint)-1; // EOF364} else {365(*env)->SetByteArrayRegion(env, ba, off, (jint)nread, (jbyte *)(buf));366}367}368369return (jint)nread;370}371372373/*374* Class: sun_tools_attach_WindowsVirtualMachine375* Method: enqueue376* Signature: (JZLjava/lang/String;[Ljava/lang/Object;)V377*/378JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_enqueue379(JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd,380jstring pipename, jobjectArray args)381{382DataBlock data;383DataBlock* pData;384DWORD* pCode;385DWORD stubLen;386HANDLE hProcess, hThread;387jint argsLen, i;388jbyte* stubCode;389jboolean isCopy;390391/*392* Setup data to copy to target process393*/394data._GetModuleHandle = _GetModuleHandle;395data._GetProcAddress = _GetProcAddress;396397strcpy(data.jvmLib, "jvm");398strcpy(data.func1, "JVM_EnqueueOperation");399strcpy(data.func2, "_JVM_EnqueueOperation@20");400401/*402* Command and arguments403*/404jstring_to_cstring(env, cmd, data.cmd, MAX_CMD_LENGTH);405argsLen = (*env)->GetArrayLength(env, args);406407if (argsLen > 0) {408if (argsLen > MAX_ARGS) {409JNU_ThrowInternalError(env, "Too many arguments");410return;411}412for (i=0; i<argsLen; i++) {413jobject obj = (*env)->GetObjectArrayElement(env, args, i);414if (obj == NULL) {415data.arg[i][0] = '\0';416} else {417jstring_to_cstring(env, obj, data.arg[i], MAX_ARG_LENGTH);418}419if ((*env)->ExceptionOccurred(env)) return;420}421}422for (i=argsLen; i<MAX_ARGS; i++) {423data.arg[i][0] = '\0';424}425426/* pipe name */427jstring_to_cstring(env, pipename, data.pipename, MAX_PIPE_NAME_LENGTH);428429/*430* Allocate memory in target process for data and code stub431* (assumed aligned and matches architecture of target process)432*/433hProcess = (HANDLE)handle;434435pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE );436if (pData == NULL) {437JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");438return;439}440WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL );441442443stubLen = (DWORD)(*env)->GetArrayLength(env, stub);444stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy);445446if ((*env)->ExceptionOccurred(env)) return;447448pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE );449if (pCode == NULL) {450JNU_ThrowIOExceptionWithLastError(env, "VirtualAllocEx failed");451VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);452return;453}454WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL );455if (isCopy) {456(*env)->ReleaseByteArrayElements(env, stub, stubCode, JNI_ABORT);457}458459/*460* Create thread in target process to execute code461*/462hThread = CreateRemoteThread( hProcess,463NULL,4640,465(LPTHREAD_START_ROUTINE) pCode,466pData,4670,468NULL );469if (hThread != NULL) {470if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) {471JNU_ThrowIOExceptionWithLastError(env, "WaitForSingleObject failed");472} else {473DWORD exitCode;474GetExitCodeThread(hThread, &exitCode);475if (exitCode) {476switch (exitCode) {477case ERR_OPEN_JVM_FAIL :478JNU_ThrowIOException(env,479"jvm.dll not loaded by target process");480break;481case ERR_GET_ENQUEUE_FUNC_FAIL :482JNU_ThrowIOException(env,483"Unable to enqueue operation: the target VM does not support attach mechanism");484break;485default :486JNU_ThrowInternalError(env,487"Remote thread failed for unknown reason");488}489}490}491CloseHandle(hThread);492} else {493if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {494//495// This error will occur when attaching to a process belonging to496// another terminal session. See "Remarks":497// http://msdn.microsoft.com/en-us/library/ms682437%28VS.85%29.aspx498//499JNU_ThrowIOException(env,500"Insufficient memory or insufficient privileges to attach");501} else {502JNU_ThrowIOExceptionWithLastError(env, "CreateRemoteThread failed");503}504}505506VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE);507VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE);508}509510/*511* Attempts to enable the SE_DEBUG_NAME privilege and open the given process.512*/513static HANDLE514doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) {515HANDLE hToken;516HANDLE hProcess = NULL;517LUID luid;518TOKEN_PRIVILEGES tp, tpPrevious;519DWORD retLength, error;520521/*522* Get the access token523*/524if (!OpenThreadToken(GetCurrentThread(),525TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,526FALSE,527&hToken)) {528if (GetLastError() != ERROR_NO_TOKEN) {529return (HANDLE)NULL;530}531532/*533* No access token for the thread so impersonate the security context534* of the process.535*/536if (!ImpersonateSelf(SecurityImpersonation)) {537return (HANDLE)NULL;538}539if (!OpenThreadToken(GetCurrentThread(),540TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,541FALSE,542&hToken)) {543return (HANDLE)NULL;544}545}546547/*548* Get LUID for the privilege549*/550if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {551error = GetLastError();552CloseHandle(hToken);553SetLastError(error);554return (HANDLE)NULL;555}556557/*558* Enable the privilege559*/560ZeroMemory(&tp, sizeof(tp));561tp.PrivilegeCount = 1;562tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;563tp.Privileges[0].Luid = luid;564565error = 0;566if (AdjustTokenPrivileges(hToken,567FALSE,568&tp,569sizeof(TOKEN_PRIVILEGES),570&tpPrevious,571&retLength)) {572/*573* If we enabled the privilege then attempt to open the574* process.575*/576if (GetLastError() == ERROR_SUCCESS) {577hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);578if (hProcess == NULL) {579error = GetLastError();580}581} else {582error = ERROR_ACCESS_DENIED;583}584585/*586* Revert to the previous privileges587*/588AdjustTokenPrivileges(hToken,589FALSE,590&tpPrevious,591retLength,592NULL,593NULL);594} else {595error = GetLastError();596}597598599/*600* Close token and restore error601*/602CloseHandle(hToken);603SetLastError(error);604605return hProcess;606}607608/* convert jstring to C string */609static void jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, int len) {610jboolean isCopy;611const char* str;612613if (jstr == NULL) {614cstr[0] = '\0';615} else {616str = JNU_GetStringPlatformChars(env, jstr, &isCopy);617if ((*env)->ExceptionOccurred(env)) return;618619strncpy(cstr, str, len);620cstr[len-1] = '\0';621if (isCopy) {622JNU_ReleaseStringPlatformChars(env, jstr, str);623}624}625}626627628