@@ -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+
138293JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_processTable (JNIEnv * env , jobject object )
139294{
140295jclass process_info_class ;
@@ -145,7 +300,8 @@ JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_proc
145300SIZE_T working_set_size , pagefile_usage ;
146301unsigned 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 TCHAR user_name [MAX_PATH ] = TEXT ("<unknown>" );
150306 TCHAR domain_name [MAX_PATH ] = TEXT ("<unknown>" );
151307 FILETIME created , exit , kernel , user ;
@@ -155,7 +311,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_proc
155311PROCESS_MEMORY_COUNTERS pmc ;
156312PROCESSENTRY32 process_entry ;
157313SID_NAME_USE sid_name_use ;
158-
314+
159315snapshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS , 0 );
160316process_entry .dwSize = sizeof (PROCESSENTRY32 );
161317if (!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