Mercurial > hg > thermostat-ng > agent
view common/portability/src/main/native/WindowsHelperImpl.c @ 2753:2f33764996f8
Port native library plugin to Windows
This patch adds Windows compatibility for the native library plugin code, and moves the Linux code to common-portability module.
Reviewed-by: sgehwolf
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-September/024893.html
author | Simon Tooke <stooke@redhat.com> |
---|---|
date | Tue, 12 Sep 2017 08:41:11 -0400 |
parents | 259b1bc3fb64 |
children |
line wrap: on
line source
/* * Copyright 2012-2017 Red Hat, Inc. * * This file is part of Thermostat. * * Thermostat is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2, or (at your * option) any later version. * * Thermostat is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Thermostat; see the file COPYING. If not see * <http://www.gnu.org/licenses/>. * * Linking this code with other modules is making a combined work * based on this code. Thus, the terms and conditions of the GNU * General Public License cover the whole combination. * * As a special exception, the copyright holders of this code give * you permission to link this code with independent modules to * produce an executable, regardless of the license terms of these * independent modules, and to copy and distribute the resulting * executable under terms of your choice, provided that you also * meet, for each linked independent module, the terms and conditions * of the license of that module. An independent module is a module * which is not derived from or based on this code. If you modify * this code, you may extend this exception to your version of the * library, but you are not obligated to do so. If you do not wish * to do so, delete this exception statement from your version. */ #include "com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl.h" #include <jni.h> #include <unistd.h> #include <string.h> #include <winsock2.h> #include <psapi.h> #include <intrin.h> #include <Sddl.h> #include <Lm.h> #include <Winternl.h> #include <wbemidl.h> // for WMI functions #if !defined(STATUS_NOT_IMPLEMENTED) # define STATUS_NOT_IMPLEMENTED 0xC0000002; #endif #define MAX_NAME 256 #ifndef NI_MAXHOST #define NI_MAXHOST 1025 #endif /* NI_MAXHOST */ // to debug the getEnvironment0() code, uncomment this //#define DEBUG_GETENV #if defined(DEBUG) static void testLength(JNIEnv* env, jlongArray array, int minLength) { // sanity test jsize len = (*env)->GetArrayLength(env, array); assert(len >= minLength); } #else static void testLength(JNIEnv* env, jlongArray array, int minLength) {} #endif /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getHostName0 * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getHostName0 (JNIEnv *env, jclass winHelperClass, jboolean prependDomain) { char hostname[NI_MAXHOST]; memset(hostname, 0, sizeof(hostname)); if (gethostname(hostname, sizeof(hostname)) == 0) { return (*env)->NewStringUTF(env, hostname); } return NULL; } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getOSVersion0 * Signature: ([J)V */ JNIEXPORT void JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getOSVersion0 (JNIEnv *env, jclass winHelperClass, jlongArray array) { testLength(env, array, 3); // Get the element pointer jlong* data = (*env)->GetLongArrayElements(env, array, 0); // NOTE: after version 8.1, this function is deprecated, and may just return the compile OS /*** OSVERSIONINFOEX vinfo; vinfo.dwOSVersionInfoSize = sizeof(vinfo); GetVersionEx((LPOSVERSIONINFO)(&vinfo)); data[0] = vinfo.dwMajorVersion; data[1] = vinfo.dwMinorVersion; data[2] = vinfo.dwBuildNumber; ***/ LPBYTE bufPtr = 0; NetWkstaGetInfo(NULL, 100, &bufPtr); data[0] = ((WKSTA_INFO_100*)(bufPtr))->wki100_ver_major; data[1] = ((WKSTA_INFO_100*)(bufPtr))->wki100_ver_minor; data[2] = 0; /* unavailable */ NetApiBufferFree(bufPtr); (*env)->ReleaseLongArrayElements(env, array, data, 0); } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getGlobalMemoryStatus0 * Signature: ([J)V */ JNIEXPORT boolean JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getGlobalMemoryStatus0 (JNIEnv *env, jclass winHelperClass, jlongArray array) { testLength(env, array, 8); // Get the element pointer jlong* data = (*env)->GetLongArrayElements(env, array, 0); // get the memory info MEMORYSTATUSEX statex; statex.dwLength = sizeof(statex); GlobalMemoryStatusEx(&statex); data[0] = statex.dwMemoryLoad; data[1] = statex.ullTotalPhys; data[2] = statex.ullAvailPhys; data[3] = statex.ullTotalPageFile; data[4] = statex.ullAvailPageFile; data[5] = statex.ullTotalVirtual; data[6] = statex.ullAvailVirtual; data[7] = statex.ullAvailExtendedVirtual; (*env)->ReleaseLongArrayElements(env, array, data, 0); return TRUE; } JNIEXPORT jint JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getLastError0 (JNIEnv *env, jclass winHelperClass) { return GetLastError(); } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getCPUString0 * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getCPUString0 (JNIEnv *env, jclass winHelperClass) { // Get extended ids. int CPUInfo[4] = {-1}; __cpuid(CPUInfo, 0x80000000); unsigned int nExIds = CPUInfo[0]; // Get the information associated with each extended ID. char CPUBrandString[0x40] = { 0 }; for(unsigned int i = 0x80000000; i <= nExIds; ++i) { __cpuid(CPUInfo, i); // Interpret CPU brand string and cache information. if (i == 0x80000002) { memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo)); } else if(i == 0x80000003) { memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo)); } else if(i == 0x80000004) { memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo)); } } return (*env)->NewStringUTF(env, CPUBrandString); } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getCPUCount0 * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getCPUCount0 (JNIEnv *env, jclass winHelperClass) { SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); return sysinfo.dwNumberOfProcessors; } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: queryPerformanceFrequency0 * Signature: ()I */ JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_queryPerformanceFrequency0 (JNIEnv *env, jclass winHelperClass) { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return (jlong)(freq.QuadPart); } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getProcessSID0 * Signature: (I)I */ JNIEXPORT jstring JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getProcessSID0 (JNIEnv *env, jclass winHelperClass, jint pid) { HANDLE hProcess; hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); if (NULL == hProcess) return NULL; HANDLE hToken = NULL; if(!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) { CloseHandle( hProcess ); return NULL; } DWORD dwLength = 0; PTOKEN_USER ptu = NULL; if (!GetTokenInformation( hToken, // handle to the access token TokenUser, // get information about the token's groups (LPVOID) ptu, // pointer to PTOKEN_USER buffer 0, // size of buffer &dwLength // receives required buffer size )) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { CloseHandle( hToken ); CloseHandle( hProcess ); return NULL; } ptu = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength); if (ptu == NULL) { CloseHandle(hToken); CloseHandle(hProcess); return NULL; } } if (!GetTokenInformation( hToken, // handle to the access token TokenUser, // get information about the token's groups (LPVOID) ptu, // pointer to PTOKEN_USER buffer dwLength, // size of buffer &dwLength // receives required buffer size )) { if (ptu != NULL) { HeapFree(GetProcessHeap(), 0, (LPVOID)ptu); } CloseHandle(hToken); CloseHandle(hProcess); return NULL; } LPWSTR stringSid; ConvertSidToStringSidW(ptu->User.Sid, &stringSid); jstring s = (*env)->NewString(env, (const jchar *)stringSid, (jsize)wcslen(stringSid)); LocalFree(stringSid); if (ptu != NULL) { HeapFree(GetProcessHeap(), 0, (LPVOID)ptu); } CloseHandle(hToken); CloseHandle(hProcess); return s; } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getUserName0 * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getUserName0 (JNIEnv *env, jclass winHelperClass, jint pid, jboolean prependDomain) { HANDLE hProcess; hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid ); if (NULL == hProcess) return NULL; HANDLE hToken = NULL; if(!OpenProcessToken( hProcess, TOKEN_QUERY, &hToken)) { CloseHandle(hProcess); return NULL; } DWORD dwLength = 0; PTOKEN_USER ptu = NULL; if (!GetTokenInformation( hToken, // handle to the access token TokenUser, // get information about the token's groups (LPVOID) ptu, // pointer to PTOKEN_USER buffer 0, // size of buffer &dwLength // receives required buffer size )) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { CloseHandle(hToken); CloseHandle(hProcess); return NULL; } ptu = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength); if (ptu == NULL) { CloseHandle(hToken); CloseHandle(hProcess); return NULL; } } if (!GetTokenInformation( hToken, // handle to the access token TokenUser, // get information about the token's groups (LPVOID) ptu, // pointer to PTOKEN_USER buffer dwLength, // size of buffer &dwLength // receives required buffer size )) { if (ptu != NULL) { HeapFree(GetProcessHeap(), 0, (LPVOID)ptu); } CloseHandle(hToken); CloseHandle(hProcess); return NULL; } DWORD dwSize = MAX_NAME; SID_NAME_USE SidType; wchar_t lpName[MAX_NAME]; wchar_t lpDomain[MAX_NAME*2 + 1]; // room for '\' + lpName jstring s = NULL; if (!LookupAccountSidW(NULL , ptu->User.Sid, lpName, &dwSize, lpDomain, &dwSize, &SidType)) { DWORD dwResult = GetLastError(); if(dwResult == ERROR_NONE_MAPPED) { wcscpy(lpName, L"NONE_MAPPED"); } else { fprintf(stderr, "LookupAccountSid Error %ld\n", GetLastError()); } } else { if (prependDomain) { wcscat(lpDomain, L"\\"); wcscat(lpDomain, lpName); s = (*env)->NewString(env, (const jchar *)lpDomain, (jsize)wcslen(lpDomain)); } else { s = (*env)->NewString(env, (const jchar *)lpName, (jsize)wcslen(lpName)); } } if (ptu != NULL) { HeapFree(GetProcessHeap(), 0, (LPVOID)ptu); } CloseHandle(hToken); CloseHandle(hProcess); return s; } /** The getEnvironment0() code is a redo of some 32-bit code examples found all over the web - updated and turned into JNI See https://blogs.msdn.microsoft.com/matt_pietrek/2004/08/25/reading-another-processs-environment/ https://sites.google.com/site/x64lab/home/notes-on-x64-windows-gui-programming/exploring-peb-process-environment-block (blog MPL) https://github.com/conix-security/zer0m0n/blob/master/src/driver/include/nt/structures/RTL_USER_PROCESS_PARAMETERS.h (GPL 3) https://blog.gapotchenko.com/eazfuscator.net/reading-environment-variables https://www.codeproject.com/Articles/25647/Read-Environment-Strings-of-Remote-Process https://www.reactos.org/ **/ /** * Wrapper to call NtQueryInformationProcess API by Run-Time Dynamic Linking * Check MSDN Documentation : http://msdn2.microsoft.com/en-us/library/ms684280(VS.85).aspx **/ NTSTATUS QueryInformationProcesss( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL ) { typedef NTSTATUS ( __stdcall *QueryInfoProcess) ( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL ); HMODULE hModNTDll = LoadLibrary("ntdll.dll"); if (!hModNTDll) { fprintf(stderr, "Error Loading library\n"); } QueryInfoProcess QueryProcInfo = (QueryInfoProcess) GetProcAddress(hModNTDll, "NtQueryInformationProcess"); if (!QueryProcInfo) { fprintf(stderr, "Can't find NtQueryInformationProcess in ntdll.dll"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS ntStat = QueryProcInfo( ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength ); FreeLibrary(hModNTDll); return ntStat; } static BOOL checkReadAccess(HANDLE hProcess, void* pAddress, int* nSize) { #if defined(DEBUG_GETENV) fprintf(stderr, "enter HasReadAccess(%ld, addr = 0x%lx\n", (long)hProcess, (long)pAddress); #endif //DEBUG_GETENV MEMORY_BASIC_INFORMATION64 memInfo; SIZE_T msize = VirtualQueryEx(hProcess, pAddress, (PMEMORY_BASIC_INFORMATION)(&memInfo), sizeof(memInfo)); if(msize == 0 || PAGE_NOACCESS == memInfo.Protect || PAGE_EXECUTE == memInfo.Protect) { *nSize = 0; fprintf(stderr, "Failed to query memory access, err=%ld prot=%ld NOACC=%ld EX=%ld msize=%ld\n", (long)GetLastError(), (long)memInfo.Protect, (long)PAGE_NOACCESS, (long)PAGE_EXECUTE, (long)msize); return FALSE; } *nSize = memInfo.RegionSize; #if defined(DEBUG_GETENV) fprintf(stderr, "exit HasReadAccess(addr = 0x%lx size = 0x%lx type=0x%lx\n", (long)pAddress, (long)(memInfo.RegionSize), (long)memInfo.Type); #endif //DEBUG_GETENV return TRUE; } typedef struct PEB64 { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[21]; PPEB_LDR_DATA LoaderData; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; //BYTE Reserved3[520]; BYTE r3[144]; DWORD OSMajor; // Warning - OSMajor/Minor/Build don't seem to be correct. DWORD OSMinor; WORD OSBuild; WORD r4; DWORD OSPlatformID; DWORD r5[3]; BYTE r6[252]; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; BYTE Reserved4[136]; ULONG SessionId; } PEB64; typedef struct RTL_USER_PROCESS_PARAMETERS64 { BYTE Reserved1[16]; BYTE Reserved2[0x28]; UNICODE_STRING CurrentWorkingDirectory; // 0x38 BYTE r3[0x18]; UNICODE_STRING ImagePathName; // 0x60 UNICODE_STRING CommandLine; // 0x70 PVOID EnvAddr; // 0x80 } RTL_USER_PROCESS_PARAMETERS64 ; /* * Caveat - does not check for invalid structures in future (or past) versions of Windows * * callmode = 0 returns DirectByteBuffer, 1 = String cwd, 2 = String execuatable, 3 = String command line * * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getEnvironment0 * Signature: ()[Ljava/lang/String; */ JNIEXPORT jobject JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getEnvironment0 (JNIEnv *env, jclass winHelperClass, jlong lhProcess, jint callmode) { HANDLE hProcess = (HANDLE)(lhProcess); BOOL isWowProcess = FALSE; IsWow64Process(hProcess, &isWowProcess); if (isWowProcess) { #if defined(DEBUG_GETENV) fprintf(stderr, "process pid=%I64d hnd=%ld is 32-bit; getEnvironment() is unsupported\n", (long long)hProcess, (long)GetProcessId(hProcess)); #endif return NULL; } PROCESS_BASIC_INFORMATION procBasicInfo = { 0 }; ULONG uReturnLength = 0; NTSTATUS ntStat = QueryInformationProcesss(hProcess, ProcessBasicInformation, &procBasicInfo, sizeof(procBasicInfo), &uReturnLength); if (ntStat != 0) { fprintf(stderr, "QueryInformationProcess() returns error 0x%0lx (id=%ld retlen=%ld PBI size=%ld)\n", (long)ntStat, (long)procBasicInfo.UniqueProcessId, (long)uReturnLength, (long)sizeof(procBasicInfo)); } #if defined(DEBUG_GETENV) fprintf(stderr, "QueryInformationProcesss() ret=%ld id=%ld retlen=%ld PBI size=%ld\n", (long)ntStat, (long)procBasicInfo.UniqueProcessId, (long)uReturnLength, (long)sizeof(procBasicInfo)); #endif //DEBUG_GETENV // Read the process environment block PEB64 procEnvBlock = { 0 }; SIZE_T returnByteCount = 0; #if defined(DEBUG_GETENV) fprintf(stderr, "reading PEB64 at 0x%ld pebsize=%ld\n", (long)procBasicInfo.PebBaseAddress, (long)sizeof(procEnvBlock)); #endif //DEBUG_GETENV // read the PEB from the other process (which is assumed to be 64-bit) if (!ReadProcessMemory(hProcess,(LPCVOID)procBasicInfo.PebBaseAddress, &procEnvBlock, sizeof(procEnvBlock), &returnByteCount)) { fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d, err=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError()); return NULL; } #if defined(DEBUG_GETENV) fprintf(stderr, "read PEB64 at 0x%ld size 0x%ld ppaddr=0x%ld\n", (long)procBasicInfo.PebBaseAddress, (long)returnByteCount, (long)procEnvBlock.ProcessParameters); fprintf(stderr, "OS is %d.%d build %d platform 0x%lx\n", procEnvBlock.OSMajor, procEnvBlock.OSMinor, procEnvBlock.OSBuild, (long)procEnvBlock.OSPlatformID); #endif //DEBUG_GETENV // Get the address of RTL_USER_PROCESS_PARAMETERS structure UCHAR* puPEB = (UCHAR*)&procEnvBlock; // retrieve a 64-bit pointer (to an usnsigned char) from PEB + 0x20 // this code appears to retrieve a 32 bit pointer UCHAR* pRTLUserInfo = (UCHAR*) *((UINT_PTR *)(puPEB + 0x20)); int readableSize = 0; #if defined(DEBUG_GETENV) fprintf(stderr, "testing RTL_USER_PROCESS_PARAMETERS memory at 0x%ld\n", (long)pRTLUserInfo); #endif //DEBUG_GETENV if (!checkReadAccess(hProcess, pRTLUserInfo, &readableSize)) { fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError()); return NULL; } #if defined(DEBUG_GETENV) fprintf(stderr, "zzz memory at 0x%ld size=0x%ld\n", (long)pRTLUserInfo, (long)readableSize); fprintf(stderr, "reading RTLUserInfo at 0x%ld size 0x%ld\n", (long)pRTLUserInfo, (long)sizeof(RTL_USER_PROCESS_PARAMETERS64)); #endif //DEBUG_GETENV // Get the first 0x64 bytes of RTL_USER_PROCESS_PARAMETERS strcuture RTL_USER_PROCESS_PARAMETERS64 upp = {0}; if (!ReadProcessMemory(hProcess, (LPCVOID)pRTLUserInfo, &upp, sizeof(RTL_USER_PROCESS_PARAMETERS64), &returnByteCount)) { fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError()); return NULL; } #if defined(DEBUG_GETENV) fprintf(stderr, "have read RTLUserInfo at 0x%ld size 0x%ld\n", (long)pRTLUserInfo, (long)returnByteCount); #endif //DEBUG_GETENV #if defined(DEBUG_GETENV) unsigned short* ch = (unsigned short*)(&upp); for (int i = 0; i < sizeof(RTL_USER_PROCESS_PARAMETERS64) / 2; i++) { fprintf(stderr, " 0x%04x = 0x%04x\n", i*2, ch[i]); } #endif //DEBUG_GETENV // cwd if (callmode == 1) { #if defined(DEBUG_GETENV) fprintf(stderr, "reading CurrentWorkingDirectory string at 0x%lx length %ld\n", (long)upp.CurrentWorkingDirectory.Buffer, (long)upp.CurrentWorkingDirectory.Length); #endif //DEBUG_GETENV WCHAR* sb = malloc(upp.CurrentWorkingDirectory.Length); if (!ReadProcessMemory(hProcess, (LPCVOID)upp.CurrentWorkingDirectory.Buffer, sb, upp.CurrentWorkingDirectory.Length, &returnByteCount)) { fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld bytes=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError(), (long)returnByteCount); free(sb); return NULL; } jstring s = (*env)->NewString(env, (const jchar *)sb, (jsize)upp.CurrentWorkingDirectory.Length / 2); free(sb); return s; } // executable image path if (callmode == 2) { #if defined(DEBUG_GETENV) fprintf(stderr, "reading string at 0x%lx length %ld\n", (long)upp.ImagePathName.Buffer, (long)upp.ImagePathName.Length); #endif //DEBUG_GETENV WCHAR* sb = malloc(upp.ImagePathName.Length); if (!ReadProcessMemory(hProcess, (LPCVOID)upp.ImagePathName.Buffer, sb, upp.ImagePathName.Length, &returnByteCount)) { fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld bytes=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError(), (long)returnByteCount); free(sb); return NULL; } jstring s = (*env)->NewString(env, (const jchar *)sb, (jsize)upp.ImagePathName.Length / 2); free(sb); return s; } // command line if (callmode == 3) { #if defined(DEBUG_GETENV) fprintf(stderr, "reading string at 0x%lx length %ld\n", (long)upp.CommandLine.Buffer, (long)upp.CommandLine.Length); #endif //DEBUG_GETENV WCHAR* sb = malloc(upp.CommandLine.Length); if (!ReadProcessMemory(hProcess, (LPCVOID)upp.CommandLine.Buffer, sb, upp.CommandLine.Length, &returnByteCount)) { fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld bytes=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError(), (long)returnByteCount); free(sb); return NULL; } jstring s = (*env)->NewString(env, (const jchar *)sb, (jsize)upp.CommandLine.Length / 2); free(sb); return s; } // Get the value at offset 0x48 to get the pointer to environment string block if (callmode == 0) { WCHAR* strPtr = (WCHAR*)(upp.EnvAddr); // find out how much we can read, and then read it // this will read more than just the environment, but there's not a lot we can do cleanly if (!checkReadAccess(hProcess, strPtr, &readableSize)) { fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld siz=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError(), (long)readableSize); return NULL; } // constrain readableSize to some maximum // 0x38000 was observed on a windows 10 system (it's about 229K, so it's huge) static const int MAX_ENV_BUFFER_SIZE = 0x38000; if (readableSize > MAX_ENV_BUFFER_SIZE) { readableSize = MAX_ENV_BUFFER_SIZE; } #if defined(DEBUG_GETENV) fprintf(stderr, "reading string at 0x%lx length %ld\n", (long)strPtr, (long)readableSize); #endif //DEBUG_GETENV WCHAR* sb = malloc(readableSize); if (!ReadProcessMemory(hProcess, (LPCVOID)strPtr, sb, readableSize, &returnByteCount)) { fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld readable=%ld retbytes=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError(), (long)readableSize, (long)returnByteCount); free(sb); return NULL; } #if defined(DEBUG_GETENV) unsigned char* ch = (unsigned char*)(sb); for (int i = 0; i < 100; i++) { fprintf(stderr, " 0x%04x = 0x%02x\n", i, ch[i]); } #endif //DEBUG_GETENV // NOTE - consider ignoring error 299 (incomplete read) and just return returnByteCount bytes here. jobject bytebuffer = (*env)->NewDirectByteBuffer(env, sb, readableSize); return bytebuffer; } return NULL; } static unsigned __int64 convertFileTimeToInt64( const FILETIME * pFileTime ) { ULARGE_INTEGER largeInt; largeInt.LowPart = pFileTime->dwLowDateTime; largeInt.HighPart = pFileTime->dwHighDateTime; return largeInt.QuadPart; } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getSystemTimes0 * array is idle, kernel, user times * Signature: ([J)V */ JNIEXPORT void JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getSystemTimes0 (JNIEnv *env, jclass winHelperClass, jlongArray times) { testLength(env, times, 3); FILETIME idleTime; FILETIME kernelTime; FILETIME userTime; GetSystemTimes(&idleTime, &kernelTime, &userTime); // Get the element pointer jlong* data = (*env)->GetLongArrayElements(env, times, 0); data[0] = convertFileTimeToInt64(&idleTime); data[1] = convertFileTimeToInt64(&kernelTime); data[2] = convertFileTimeToInt64(&userTime); (*env)->ReleaseLongArrayElements(env, times, data, 0); } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getCPUUsagePercent0 * Signature: ([[I)I */ JNIEXPORT jint JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getCPUUsagePercent0 (JNIEnv *env, jclass winHelperClass, jobjectArray arrayOfArrayOfInts) { int cpuIdx = 0; const jsize arraylen = (*env)->GetArrayLength(env, arrayOfArrayOfInts); // result code from COM calls HRESULT hr = 0; // COM interface pointers IWbemLocator *locator = NULL; IWbemServices *services = NULL; IEnumWbemClassObject *results = NULL; // BSTR strings we'll use (http://msdn.microsoft.com/en-us/library/ms221069.aspx) BSTR resource = SysAllocString(L"ROOT\\CIMV2"); BSTR language = SysAllocString(L"WQL"); BSTR query = SysAllocString(L"SELECT Name, PercentIdleTime, PercentPrivilegedTime, PercentUserTime FROM Win32_PerfFormattedData_Counters_ProcessorInformation"); // initialize COM hr = CoInitializeEx(0, COINIT_MULTITHREADED); hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); // connect to WMI hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *) &locator); hr = locator->lpVtbl->ConnectServer(locator, resource, NULL, NULL, NULL, 0, NULL, NULL, &services); // issue a WMI query hr = services->lpVtbl->ExecQuery(services, language, query, WBEM_FLAG_BIDIRECTIONAL, NULL, &results); // list the query results if (results != NULL) { IWbemClassObject *result = NULL; ULONG returnedCount = 0; // enumerate the retrieved objects while((hr = results->lpVtbl->Next(results, WBEM_INFINITE, 1, &result, &returnedCount)) == S_OK) { if (cpuIdx >= arraylen) { // we've overflowed - only return what the array can hold break; } VARIANT name; VARIANT idlePercent; VARIANT systemPercent; VARIANT userPercent; // obtain the desired properties of the next result and print them out hr = result->lpVtbl->Get(result, L"Name", 0, &name, 0, 0); // ignore _Total rows if (wcsstr(name.bstrVal, L"_Total") == NULL) { hr = result->lpVtbl->Get(result, L"PercentIdleTime", 0, &idlePercent, 0, 0); hr = result->lpVtbl->Get(result, L"PercentPrivilegedTime", 0, &systemPercent, 0, 0); hr = result->lpVtbl->Get(result, L"PercentUserTime", 0, &userPercent, 0, 0); const int idlePct = _wtoi(idlePercent.bstrVal); const int sysPct = _wtoi(systemPercent.bstrVal); const int userPct = _wtoi(userPercent.bstrVal); jintArray arrayObj = (jintArray)(*env)->GetObjectArrayElement(env, arrayOfArrayOfInts, cpuIdx); testLength(env, arrayObj, 3); jint* array = (*env)->GetIntArrayElements(env, arrayObj, NULL); array[0] = idlePct; array[1] = sysPct; array[2] = userPct; (*env)->ReleaseIntArrayElements(env, arrayObj, array, 0); cpuIdx++; // only count cpus, not totals or subtotals } // release the current result object result->lpVtbl->Release(result); } } // release WMI COM interfaces results->lpVtbl->Release(results); services->lpVtbl->Release(services); locator->lpVtbl->Release(locator); // unwind everything else we've allocated CoUninitialize(); SysFreeString(query); SysFreeString(language); SysFreeString(resource); return (jint)(cpuIdx); } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getProcessMemoryInfo0 * Signature: (I[J)V */ JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getProcessInfo0 (JNIEnv *env, jclass winHelperClass, jint pid, jlongArray array) { testLength(env, array, 4); HANDLE hProcess; PROCESS_MEMORY_COUNTERS pmc; hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid ); if (NULL == hProcess) return FALSE; // Get the element pointer jlong* data = (*env)->GetLongArrayElements(env, array, 0); pmc.cb = sizeof(pmc); if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) { data[0] = pmc.WorkingSetSize; } else { (*env)->ReleaseLongArrayElements(env, array, data, 0); return FALSE; } FILETIME creationTime; FILETIME exitTime; FILETIME kernelTime; FILETIME userTime; if (GetProcessTimes( hProcess, &creationTime, &exitTime, &kernelTime, &userTime)) { // times returned from GetProcessTimes() are in 100-nanosecond units. data[1] = convertFileTimeToInt64(&userTime); data[2] = convertFileTimeToInt64(&kernelTime); data[3] = convertFileTimeToInt64(&creationTime); data[4] = 10000000; // 100 nanonseconds is this many ticks per second } CloseHandle(hProcess); (*env)->ReleaseLongArrayElements(env, array, data, 0); return TRUE; } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getProcessIOInfo0 * Signature: (I[J)V */ JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getProcessIOInfo0 (JNIEnv *env, jclass winHelperClass, jint pid, jlongArray array) { testLength(env, array, 6); HANDLE hProcess; IO_COUNTERS iocounters; hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); if (NULL == hProcess) return FALSE; BOOL rc = GetProcessIoCounters(hProcess, &iocounters); if (!rc) { CloseHandle(hProcess); return FALSE; } // Get the element pointer jlong* data = (*env)->GetLongArrayElements(env, array, 0); data[0] = iocounters.ReadOperationCount; data[1] = iocounters.WriteOperationCount; data[2] = iocounters.ReadTransferCount; data[3] = iocounters.WriteTransferCount; data[4] = iocounters.OtherOperationCount; data[5] = iocounters.OtherTransferCount; (*env)->ReleaseLongArrayElements(env, array, data, 0); CloseHandle(hProcess); return TRUE; } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getProcessHandle0 * Signature: (I)J */ JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getCurrentProcessHandle0 (JNIEnv *env, jclass winHelperClass) { HANDLE hProcess; hProcess = GetCurrentProcess(); return (jlong) hProcess; } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getProcessHandle0 * Signature: (I)J */ JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getProcessHandle0 (JNIEnv *env, jclass winHelperClass, jint pid) { HANDLE hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); return (jlong) hProcess; } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getCurrentProcessID0 * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getCurrentProcessID0 (JNIEnv *env, jclass winHelperClass) { return (jint) GetCurrentProcessId(); } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getLimitedProcessHandle0 * Signature: (I)J */ JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getLimitedProcessHandle0 (JNIEnv *env, jclass winHelperClass, jint pid) { HANDLE hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); return (jlong) hProcess; } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: exists0 * Signature: (I)I */ JNIEXPORT jint JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_exists0 (JNIEnv *env, jclass winHelperClass, jint pid) { if (pid == 0) return 1; HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); if (hProcess == 0) return 0; LPDWORD ret = 0; int rc = GetExitCodeProcess(hProcess, &ret); CloseHandle(hProcess); if (rc) { return ret == STILL_ACTIVE; } else { return 0; } } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: closeHandle0 * Signature: (J)V */ JNIEXPORT void JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_closeHandle0 (JNIEnv *env, jclass winHelperClass, jlong handle) { CloseHandle((HANDLE)handle); } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: terminateProcess0 * Signature: (IIB)V */ JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_terminateProcess0 (JNIEnv *env, jclass winHelperClass, jint pid, jint exitCode, jint waitMillis) { HANDLE hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); if (!hProcess) { return FALSE; } TerminateProcess(hProcess, (unsigned int)exitCode); if (waitMillis >= 0) { WaitForSingleObject(hProcess, waitMillis); } CloseHandle(hProcess); return TRUE; } /* * Class: Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: freeDirectBuffer0 * Signature: (Ljava/nio/ByteBuffer;)V */ JNIEXPORT void JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_freeDirectBuffer0 (JNIEnv *env, jobject obj, jobject bytebuffer) { void *buffer = (*env)->GetDirectBufferAddress(env, bytebuffer); free(buffer); } /* * Class: com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl * Method: getModules0 * Signature: (J)[Ljava/lang/String; */ JNIEXPORT jobjectArray JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getModules0 (JNIEnv *env, jclass klass, jint pid) { // Get a handle to the process (current process if pid = 0) HANDLE hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); if (NULL == hProcess) { return NULL; } jobjectArray ret = NULL; // Get a list of all the modules in this process. DWORD cbNeeded = 0; if (EnumProcessModules(hProcess, NULL, 0, &cbNeeded)) { HMODULE* hMods = malloc(cbNeeded); DWORD retBytes = 0; // potential chase here: the library list can change size between these two calls. if (EnumProcessModules(hProcess, hMods, cbNeeded, &retBytes)) { int nModules = (retBytes / sizeof(HMODULE)); ret = (jobjectArray) (*env)->NewObjectArray(env, nModules, (*env)->FindClass(env, "java/lang/String"), NULL); for (int i = 0; i < nModules; i++) { TCHAR szModName[MAX_PATH]; // Get the full path to the module's file. int nchar = GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR)); if (nchar > 0) { // Print the module name and handle value. jstring s = (*env)->NewString(env, (const jchar *)szModName, (jsize)wcslen(szModName)); s = (*env)->NewStringUTF(env, szModName); (*env)->SetObjectArrayElement(env, ret, i, s); } } } free(hMods); } // Release the handle to the process. CloseHandle( hProcess ); return ret; }