451
#include <tchar.h>
452
#include <strsafe.h>
453
454
+WCHAR* GetProcessCommandLine(HANDLE hProcess);
455
+
456
struct perflib_data {
457
PERF_DATA_BLOCK *pDataBlock;
458
PERF_OBJECT_TYPE *pObjectType;
460
DWORD pid;
461
};
462
461
-BOOL EnableDebugPrivilege() {
462
- HANDLE hToken;
463
- LUID luid;
464
- TOKEN_PRIVILEGES tkp;
465
-
466
- if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
467
- return FALSE;
468
-
469
- if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
470
- return FALSE;
471
-
472
- tkp.PrivilegeCount = 1;
473
- tkp.Privileges[0].Luid = luid;
474
- tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
475
-
476
- if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
477
- return FALSE;
478
-
479
- CloseHandle(hToken);
480
-
481
- return TRUE;
482
-}
483
-
463
void apps_os_init_windows(void) {
464
PerflibNamesRegistryInitialize();
465
487
- if(!EnableDebugPrivilege())
488
- nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable debug privilege");
466
+ if(!EnableWindowsPrivilege(SE_DEBUG_NAME))
467
+ nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable %s privilege", SE_DEBUG_NAME);
468
+
469
+ if(!EnableWindowsPrivilege(SE_SYSTEM_PROFILE_NAME))
470
+ nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable %s privilege", SE_SYSTEM_PROFILE_NAME);
471
+
472
+ if(!EnableWindowsPrivilege(SE_PROF_SINGLE_PROCESS_NAME))
473
+ nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable %s privilege", SE_PROF_SINGLE_PROCESS_NAME);
474
}
475
476
uint64_t apps_os_get_total_memory_windows(void) {
485
return memStat.ullTotalPhys;
486
}
487
503
-static __thread wchar_t unicode[PATH_MAX];
488
+// remove the PID suffix and .exe suffix, if any
489
+static void fix_windows_comm(struct pid_stat *p, char *comm) {
490
+ char pid[UINT64_MAX_LENGTH + 1]; // +1 for the underscore
491
+ pid[0] = '_';
492
+ print_uint64(&pid[1], p->pid);
493
+ size_t pid_len = strlen(pid);
494
+ size_t comm_len = strlen(comm);
495
+ if (pid_len < comm_len) {
496
+ char *compare = &comm[comm_len - pid_len];
497
+ if (strcmp(pid, compare) == 0)
498
+ *compare = '\0';
499
+ }
500
+
501
+ // remove the .exe suffix, if any
502
+ comm_len = strlen(comm);
503
+ size_t exe_len = strlen(".exe");
504
+ if(exe_len < comm_len) {
505
+ char *compare = &comm[comm_len - exe_len];
506
+ if (strcmp(".exe", compare) == 0)
507
+ *compare = '\0';
508
+ }
509
+}
510
511
// Convert wide string to UTF-8
506
-static STRING *wchar_to_string(WCHAR *s) {
512
+static char *wchar_to_utf8(WCHAR *s) {
513
static __thread char utf8[PATH_MAX];
514
static __thread int utf8_size = sizeof(utf8);
515
518
return NULL;
519
520
WideCharToMultiByte(CP_UTF8, 0, s, -1, utf8, utf8_size, NULL, NULL);
515
- return string_strdupz(utf8);
521
+ return utf8;
522
}
523
518
-STRING *GetProcessFriendlyName(WCHAR *path) {
524
+// Convert wide string to UTF-8
525
+static STRING *wchar_to_string(WCHAR *s) {
526
+ return string_strdupz(wchar_to_utf8(s));
527
+}
528
+
529
+// --------------------------------------------------------------------------------------------------------------------
530
+
531
+// return a sanitized name for the process
532
+STRING *GetProcessFriendlyNameSanitized(WCHAR *path) {
533
static __thread uint8_t void_buf[1024 * 1024];
534
+ static __thread DWORD void_buf_size = sizeof(void_buf);
535
+ static __thread wchar_t unicode[PATH_MAX];
536
+ static __thread DWORD unicode_size = sizeof(unicode) / sizeof(*unicode);
537
538
DWORD handle;
539
DWORD size = GetFileVersionInfoSizeW(path, &handle);
523
- if (size == 0 || size > sizeof(void_buf))
540
+ if (size == 0 || size > void_buf_size)
541
return FALSE;
542
543
if (GetFileVersionInfoW(path, handle, size, void_buf)) {
544
LPWSTR value = NULL;
545
UINT len = 0;
529
- DWORD unicode_size = sizeof(unicode) / sizeof(*unicode);
546
if (VerQueryValueW(void_buf, L"\\StringFileInfo\\040904B0\\FileDescription", (LPVOID*)&value, &len) &&
547
len > 0 && len < unicode_size) {
548
wcsncpy(unicode, value, unicode_size - 1);
549
unicode[unicode_size - 1] = L'\0';
534
- return wchar_to_string(unicode);
550
+ char *name = wchar_to_utf8(unicode);
551
+ sanitize_chart_meta(name);
552
+ return string_strdupz(name);
553
+ }
554
+ }
555
+
556
+ return NULL;
557
+}
558
+
559
+#define SERVICE_PREFIX "Service "
560
+// return a sanitized name for the process
561
+static STRING *GetNameFromCmdlineSanitized(struct pid_stat *p) {
562
+ if(!p->cmdline) return NULL;
563
+
564
+ char buf[string_strlen(p->cmdline) + 1];
565
+ memcpy(buf, string2str(p->cmdline), sizeof(buf));
566
+ char *words[100];
567
+ size_t num_words = quoted_strings_splitter(buf, words, 100, isspace_map_pluginsd);
568
+
569
+ if(string_strcmp(p->comm, "svchost") == 0) {
570
+ // find -s SERVICE in the command line
571
+ for(size_t i = 0; i < num_words ;i++) {
572
+ if(strcmp(words[i], "-s") == 0 && i + 1 < num_words) {
573
+ char service[strlen(words[i + 1]) + sizeof(SERVICE_PREFIX)]; // sizeof() includes a null
574
+ strcpy(service, SERVICE_PREFIX);
575
+ strcpy(&service[sizeof(SERVICE_PREFIX) - 1], words[i + 1]);
576
+ sanitize_chart_meta(service);
577
+ return string_strdupz(service);
578
+ }
579
}
580
}
581
582
return NULL;
583
}
584
585
+static void GetServiceNames(void) {
586
+ SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
587
+ if (hSCManager == NULL) return;
588
+
589
+ DWORD dwBytesNeeded = 0, dwServicesReturned = 0, dwResumeHandle = 0;
590
+ ENUM_SERVICE_STATUS_PROCESS *pServiceStatus = NULL;
591
+
592
+ // First, query the required buffer size
593
+ EnumServicesStatusEx(
594
+ hSCManager, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL,
595
+ NULL, 0, &dwBytesNeeded, &dwServicesReturned, &dwResumeHandle, NULL);
596
+
597
+ if (dwBytesNeeded == 0) {
598
+ CloseServiceHandle(hSCManager);
599
+ return;
600
+ }
601
+
602
+ // Allocate memory to hold the services
603
+ pServiceStatus = mallocz(dwBytesNeeded);
604
+
605
+ // Now, retrieve the list of services
606
+ if (!EnumServicesStatusEx(
607
+ hSCManager, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL,
608
+ (LPBYTE)pServiceStatus, dwBytesNeeded, &dwBytesNeeded, &dwServicesReturned,
609
+ &dwResumeHandle, NULL)) {
610
+ freez(pServiceStatus);
611
+ CloseServiceHandle(hSCManager);
612
+ return;
613
+ }
614
+
615
+ // Loop through the services
616
+ for (DWORD i = 0; i < dwServicesReturned; i++) {
617
+ if(!pServiceStatus[i].lpDisplayName || !*pServiceStatus[i].lpDisplayName)
618
+ continue;
619
+
620
+ struct pid_stat *p = find_pid_entry((pid_t)pServiceStatus[i].ServiceStatusProcess.dwProcessId);
621
+ if(p && !p->got_service) {
622
+ p->got_service = true;
623
+
624
+ size_t len = strlen(pServiceStatus[i].lpDisplayName);
625
+ char buf[len + 1];
626
+ memcpy(buf, pServiceStatus[i].lpDisplayName, sizeof(buf));
627
+ sanitize_chart_meta(buf);
628
+
629
+ string_freez(p->name);
630
+ p->name = string_strdupz(buf);
631
+ }
632
+ }
633
+
634
+ free(pServiceStatus);
635
+ CloseServiceHandle(hSCManager);
636
+}
637
+
638
+static WCHAR *executable_path_from_cmdline(WCHAR *cmdline) {
639
+ if (!cmdline || !*cmdline) return NULL;
640
+
641
+ WCHAR *exe_path_start = cmdline;
642
+ WCHAR *exe_path_end = NULL;
643
+
644
+ if (cmdline[0] == L'"') {
645
+ // Command line starts with a double quote
646
+ exe_path_start++; // Move past the first double quote
647
+ exe_path_end = wcschr(exe_path_start, L'"'); // Find the next quote
648
+ }
649
+ else {
650
+ // Command line does not start with a double quote
651
+ exe_path_end = wcschr(exe_path_start, L' '); // Find the first space
652
+ }
653
+
654
+ if (exe_path_end) {
655
+ // Null-terminate the string at the end of the executable path
656
+ *exe_path_end = L'\0';
657
+ return exe_path_start;
658
+ }
659
+
660
+ return NULL;
661
+}
662
+
663
void GetAllProcessesInfo(void) {
664
+ static __thread wchar_t unicode[PATH_MAX];
665
+ static __thread DWORD unicode_size = sizeof(unicode) / sizeof(*unicode);
666
+
667
calls_counter++;
668
669
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
677
return;
678
}
679
680
+ bool need_service_names = false;
681
+
682
do {
683
+ if(!pe32.th32ProcessID) continue;
684
+
685
struct pid_stat *p = get_or_allocate_pid_entry((pid_t)pe32.th32ProcessID);
686
p->ppid = (pid_t)pe32.th32ParentProcessID;
687
if(p->got_info) continue;
688
p->got_info = true;
689
561
- if(!p->initialized) {
562
- string_freez(p->comm);
563
- p->comm = wchar_to_string(pe32.szExeFile);
564
- p->assigned_to_target = false;
565
- }
566
-
690
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, p->pid);
568
- if (hProcess == NULL) continue;
691
+ if (hProcess == NULL)
692
+ continue;
693
570
- STRING *full_path = NULL;
571
- STRING *friendly_name = NULL;
694
+ // Get the full command line, if possible
695
+ {
696
+ WCHAR *cmdline = GetProcessCommandLine(hProcess); // returns malloc'd buffer
697
+ if (cmdline) {
698
+ string_freez(p->cmdline);
699
+ p->cmdline = wchar_to_string(cmdline);
700
+
701
+ // extract the process full path from the command line
702
+ WCHAR *path = executable_path_from_cmdline(cmdline);
703
+ if(path) {
704
+ string_freez(p->name);
705
+ p->name = GetProcessFriendlyNameSanitized(path);
706
+ }
707
573
- DWORD unicode_size = sizeof(unicode) / sizeof(*unicode);
574
- if(QueryFullProcessImageNameW(hProcess, 0, unicode, &unicode_size)) {
575
- full_path = wchar_to_string(unicode);
576
- friendly_name = GetProcessFriendlyName(unicode);
708
+ free(cmdline); // free(), not freez()
709
+ }
710
}
711
579
- CloseHandle(hProcess);
712
+ if(!p->cmdline || !p->name) {
713
+ if (QueryFullProcessImageNameW(hProcess, 0, unicode, &unicode_size)) {
714
+ // put the full path name to the command into cmdline
715
+ if(!p->cmdline)
716
+ p->cmdline = wchar_to_string(unicode);
717
581
- if(full_path) {
582
- string_freez(p->cmdline);
583
- p->cmdline = full_path;
718
+ if(!p->name)
719
+ p->name = GetProcessFriendlyNameSanitized(unicode);
720
+ }
721
}
722
586
- if(friendly_name) {
723
+ CloseHandle(hProcess);
724
+
725
+ char *comm = wchar_to_utf8(pe32.szExeFile);
726
+ fix_windows_comm(p, comm);
727
+ update_pid_comm(p, comm); // will sanitize p->comm
728
+
729
+ if(!need_service_names && string_strcmp(p->comm, "svchost") == 0)
730
+ need_service_names = true;
731
+
732
+ STRING *better_name = GetNameFromCmdlineSanitized(p);
733
+ if(better_name) {
734
string_freez(p->name);
588
- p->name = friendly_name;
589
- p->assigned_to_target = false;
735
+ p->name = better_name;
736
}
737
+
738
} while (Process32NextW(hSnapshot, &pe32));
739
740
CloseHandle(hSnapshot);
741
+
742
+ if(need_service_names)
743
+ GetServiceNames();
744
}
745
746
static inline kernel_uint_t perflib_cpu_utilization(COUNTER_DATA *d) {
842
// a new pid
843
p->initialized = true;
844
695
- static __thread char name[MAX_PATH];
696
-
697
- if (getInstanceName(d.pDataBlock, d.pObjectType, d.pi, name, sizeof(name))) {
698
- // remove the PID suffix, if any
699
- char pid[UINT64_MAX_LENGTH + 1]; // +1 for the underscore
700
- pid[0] = '_';
701
- print_uint64(&pid[1], p->pid);
702
- size_t pid_len = strlen(pid);
703
- size_t name_len = strlen(name);
704
- if (pid_len < name_len) {
705
- char *compare = &name[name_len - pid_len];
706
- if (strcmp(pid, compare) == 0)
707
- *compare = '\0';
708
- }
845
+ static __thread char comm[MAX_PATH];
846
710
- // remove the .exe suffix, if any
711
- name_len = strlen(name);
712
- size_t exe_len = strlen(".exe");
713
- if(exe_len < name_len) {
714
- char *compare = &name[name_len - exe_len];
715
- if (strcmp(".exe", compare) == 0)
716
- *compare = '\0';
717
- }
718
- }
847
+ if (getInstanceName(d.pDataBlock, d.pObjectType, d.pi, comm, sizeof(comm)))
848
+ fix_windows_comm(p, comm);
849
else
720
- strncpyz(name, "unknown", sizeof(name) - 1);
850
+ strncpyz(comm, "unknown", sizeof(comm) - 1);
851
722
- if(strcmp(name, "wininit") == 0)
852
+ if(strcmp(comm, "wininit") == 0)
853
INIT_PID = p->pid;
854
725
- string_freez(p->comm); // it may be detected in a previous run via GetAllProcessesInfo()
726
- p->comm = string_strdupz(name);
727
- p->got_info = false;
728
- p->assigned_to_target = false;
855
+ update_pid_comm(p, comm); // will sanitize p->comm
856
added++;
857
858
COUNTER_DATA ppid = {.key = "Creating Process ID"};