Skip to content

Commit 41b71f1

Browse files
committed
Merge pull request #16 from EleotleCram/win-commandline-fix
Fixes #15 : ProcessInfo.html#getCommand() returns wrong information on W...
2 parents 78f02f0 + 8826b8c commit 41b71f1

File tree

3 files changed

+168
-7
lines changed

3 files changed

+168
-7
lines changed

lib/native/javasysmon.dll

-8 KB
Binary file not shown.

lib/native/javasysmon64.dll

-2 KB
Binary file not shown.

src/main/c/windows/javasysmon.c

Lines changed: 168 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,161 @@ JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_currentPid (
135135
return (jint) current_pid;
136136
}
137137

138+
/**
139+
* Converts the given UTF-16 string to UTF-8. Returns ZERO upon success, NON-ZERO otherwise.
140+
* Upon success, the parameter utf8 points to the UTF-8 string. It is the calling function's responsibility that this resource is freed.
141+
* Upon error the parameter utf8 will be set to NULL.
142+
*/
143+
DWORD WideCharToUTF8(wchar_t* utf16, char** utf8)
144+
{
145+
int utf8_length;
146+
147+
*utf8 = NULL;
148+
149+
utf8_length = WideCharToMultiByte(
150+
CP_UTF8, // Convert to UTF-8
151+
0, // No special character conversions required
152+
// (UTF-16 and UTF-8 support the same characters)
153+
utf16, // UTF-16 string to convert
154+
-1, // utf16 is NULL terminated (if not, use length)
155+
NULL, // Determining correct output buffer size
156+
0, // Determining correct output buffer size
157+
NULL, // Must be NULL for CP_UTF8
158+
NULL); // Must be NULL for CP_UTF8
159+
160+
if (utf8_length == 0) {
161+
// Error - call GetLastError for details
162+
return GetLastError();
163+
}
164+
165+
*utf8 = (char*)malloc(sizeof(char) * utf8_length); // Allocate space for UTF-8 string
166+
167+
utf8_length = WideCharToMultiByte(
168+
CP_UTF8, // Convert to UTF-8
169+
0, // No special character conversions required
170+
// (UTF-16 and UTF-8 support the same characters)
171+
utf16, // UTF-16 string to convert
172+
-1, // utf16 is NULL terminated (if not, use length)
173+
*utf8, // UTF-8 output buffer
174+
utf8_length, // UTF-8 output buffer size
175+
NULL, // Must be NULL for CP_UTF8
176+
NULL); // Must be NULL for CP_UTF8
177+
178+
if (utf8_length == 0) {
179+
// Error - call GetLastError for details
180+
free(*utf8);
181+
*utf8 = NULL;
182+
return GetLastError();
183+
}
184+
185+
return 0;
186+
}
187+
188+
typedef struct _LSA_UNICODE_STRING {
189+
USHORT Length;
190+
USHORT MaximumLength;
191+
PWSTR Buffer;
192+
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
193+
194+
typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
195+
HANDLE ProcessHandle,
196+
DWORD ProcessInformationClass,
197+
PVOID ProcessInformation,
198+
DWORD ProcessInformationLength,
199+
PDWORD ReturnLength
200+
);
201+
202+
typedef struct _PROCESS_BASIC_INFORMATION
203+
{
204+
LONG ExitStatus;
205+
PVOID PebBaseAddress;
206+
ULONG_PTR AffinityMask;
207+
LONG BasePriority;
208+
ULONG_PTR UniqueProcessId;
209+
ULONG_PTR ParentProcessId;
210+
} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
211+
212+
/**
213+
* Retrieve the original command line from the process environment block (PEB). Returns ZERO upon success, NON-ZERO otherwise.
214+
* Upon success, the parameter commandLine points to the original command line string (UTF-16) that was used to start the given process.
215+
* It is the calling function's responsibility that this resource is freed.
216+
* Upon error the parameter commandLine will be set to NULL.
217+
*/
218+
DWORD GetCommandLineFromPeb(DWORD dwPid, wchar_t** commandLine)
219+
{
220+
DWORD dw;
221+
#ifdef _WIN64
222+
SIZE_T read;
223+
#else
224+
DWORD read;
225+
#endif
226+
HANDLE hProcess;
227+
_NtQueryInformationProcess pNtQip;
228+
PROCESS_BASIC_INFORMATION pbInfo;
229+
UNICODE_STRING cmdline;
230+
WCHAR* wcmdLine;
231+
232+
*commandLine = NULL;
233+
234+
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPid );
235+
if( !hProcess ) {
236+
return GetLastError();
237+
}
238+
239+
pNtQip = (_NtQueryInformationProcess) GetProcAddress(GetModuleHandleA("ntdll.dll"),
240+
"NtQueryInformationProcess");
241+
if(!pNtQip) {
242+
return GetLastError();
243+
}
244+
245+
pNtQip(hProcess, 0, &pbInfo, sizeof(pbInfo), NULL);
246+
247+
#ifdef _WIN64
248+
ReadProcessMemory(hProcess, (PCHAR)(pbInfo.PebBaseAddress) + 0x20, &dw, sizeof(dw), &read);
249+
#else
250+
ReadProcessMemory(hProcess, (PCHAR)(pbInfo.PebBaseAddress) + 0x10, &dw, sizeof(dw),&read);
251+
#endif
252+
253+
#ifdef _WIN64
254+
ReadProcessMemory(hProcess, (PCHAR)dw+112, &cmdline, sizeof(cmdline), &read);
255+
#else
256+
ReadProcessMemory(hProcess, (PCHAR)dw+64, &cmdline, sizeof(cmdline), &read);
257+
#endif
258+
259+
wcmdLine = (WCHAR *)malloc(sizeof(char)*(cmdline.Length + 2));
260+
if( !wcmdLine )
261+
return -1;
262+
263+
ReadProcessMemory(hProcess, (PVOID)cmdline.Buffer, wcmdLine,
264+
cmdline.Length+2, &read);
265+
266+
*commandLine = wcmdLine;
267+
268+
CloseHandle(hProcess);
269+
270+
return 0;
271+
}
272+
273+
/**
274+
* Retrieve the original command line from the process environment block (PEB). Returns ZERO upon success, NON-ZERO otherwise.
275+
*
276+
* This function is similar to GetCommandLineFromPeb, but returns an UTF-8 string instead.
277+
*/
278+
DWORD GetCommandLineUTF8(DWORD dwPid, char** utf8CommandLine) {
279+
wchar_t* wcCommandLine;
280+
281+
if(!GetCommandLineFromPeb(dwPid, &wcCommandLine)) {
282+
if(!WideCharToUTF8(wcCommandLine, utf8CommandLine)) {
283+
free(wcCommandLine);
284+
return 0;
285+
} else {
286+
free(wcCommandLine);
287+
}
288+
}
289+
290+
return GetLastError();
291+
}
292+
138293
JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_processTable (JNIEnv *env, jobject object)
139294
{
140295
jclassprocess_info_class;
@@ -145,7 +300,8 @@ JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_proc
145300
SIZE_Tworking_set_size, pagefile_usage;
146301
unsigned int i, ppid;
147302
TCHAR process_name[MAX_PATH] = TEXT("<unknown>");
148-
TCHAR process_command[MAX_PATH] = TEXT("<unknown>");
303+
TCHAR process_command[MAX_PATH+1] = TEXT("<unknown>");
304+
char*process_command_raw;
149305
TCHARuser_name[MAX_PATH] = TEXT("<unknown>");
150306
TCHARdomain_name[MAX_PATH] = TEXT("<unknown>");
151307
FILETIME created, exit, kernel, user;
@@ -155,7 +311,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_proc
155311
PROCESS_MEMORY_COUNTERS pmc;
156312
PROCESSENTRY32 process_entry;
157313
SID_NAME_USEsid_name_use;
158-
314+
159315
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
160316
process_entry.dwSize = sizeof(PROCESSENTRY32);
161317
if (!EnumProcesses(processes, sizeof(processes), &buffer_size))
@@ -168,7 +324,6 @@ JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_proc
168324
working_set_size = pagefile_usage = ppid = 0;
169325
user_token = NULL;
170326
// &process_name = TEXT("<unknown>");
171-
// &process_command = TEXT("<unknown>");
172327
// You can't get ppid from the usual PSAPI calls, so you need to use the ToolHelp stuff
173328
// Thanks to http://www.codeproject.com/KB/threads/ParentPID.aspx?msg=1637993 for the tip
174329
if (Process32First(snapshot, &process_entry)) {
@@ -185,8 +340,14 @@ JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_proc
185340
if (EnumProcessModules(process, &module, sizeof(module), &buffer_size)) {
186341
GetModuleBaseName(process, module, process_name, sizeof(process_name) / sizeof(TCHAR));
187342
}
188-
// get command name
189-
GetProcessImageFileName(process, process_command, sizeof(process_command) / sizeof(TCHAR));
343+
// get command name and copy to memory on the stack
344+
// (somehow NewStringUTF(process_command_raw); free(process_command_raw); segfaults, and this doesn't)
345+
GetCommandLineUTF8(processes[i], &process_command_raw);
346+
if(process_command_raw) {
347+
memcpy(process_command, process_command_raw, MAX_PATH);
348+
process_command[MAX_PATH] = '\0';
349+
free(process_command_raw);
350+
}
190351
// get CPU usage
191352
GetProcessTimes(process, &created, &exit, &kernel, &user);
192353
// get owner (thanks to http://www.codeproject.com/KB/cs/processownersid.aspx)
@@ -225,13 +386,13 @@ JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_proc
225386
"(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJJ)V");
226387
process_info = (*env)->NewObject(env, process_info_class, process_info_constructor, (jint) processes[i],
227388
(jint) ppid, // parent id
228-
(*env)->NewStringUTF(env, process_command), // command
389+
(*env)->NewStringUTF(env, process_command == NULL ? "<unknown>" : process_command), // command
229390
(*env)->NewStringUTF(env, process_name), // name
230391
(*env)->NewStringUTF(env, user_token == NULL ? "<unknown>" : domain_name), // owner
231392
(jlong) filetime_to_millis(&user), // user millis
232393
(jlong) filetime_to_millis(&kernel), // system millis
233394
(jlong) working_set_size, // resident bytes
234-
(jlong) working_set_size + pagefile_usage); // total bytes
395+
(jlong) working_set_size + pagefile_usage); // total bytes
235396
(*env)->SetObjectArrayElement(env, process_info_array, i, process_info);
236397
(*env)->DeleteLocalRef(env, process_info_class);
237398
}

0 commit comments

Comments
 (0)