| /* ********************************************************** |
| * Copyright (c) 2012-2019 Google, Inc. All rights reserved. |
| * Copyright (c) 2003-2008 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| /* |
| * DRview.c |
| * |
| * command-line tool for determining what is running under DR |
| * |
| * (c) araksha, inc. all rights reserved |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <tchar.h> |
| |
| /* The order here now matters: share.h needs to be the first to include |
| * globals_shared.h, as it has to set HOT_PATCHING_INTERFACE first; |
| * but ntdll.h now includes globals_shared.h, so we put it second. |
| * But, processes.h duplicates _VM_COUNTERS from ntdll.h but can handle |
| * going 2nd but not first: so it must go last. |
| */ |
| #include "share.h" |
| #include "ntdll.h" |
| #include "processes.h" |
| |
| #define NAME "DR" |
| |
| int |
| procwalk(); |
| void |
| dllwalk(); |
| |
| void |
| usage() |
| { |
| fprintf(stderr, "Usage:\n"); |
| fprintf(stderr, |
| "DRview [-help] [-pid n] [-exe name] [-listdr] [-listall] [-listdlls] " |
| "[-showdlls] [-nopid] [-no32] [-out file] [-cmdline] [-showmem] [-showtime] " |
| "[-nobuildnum] [-qname strip] [-noqnames] [-hot_patch] [-s n] [-tillidle] " |
| "[-idlecpu c] [-showmemfreq f] [-idleafter s] [-v]\n"); |
| exit(1); |
| } |
| |
| void |
| help() |
| { |
| fprintf(stderr, "Options:\n"); |
| fprintf(stderr, " -pid n\t\t\tdisplays whether the process is injected into\n"); |
| fprintf(stderr, " -exe name\t\tfinds all processes whose executable matches\n"); |
| fprintf(stderr, "\t\t\t'name', shows whether injected into\n"); |
| fprintf(stderr, " -listdr\t\tlist all processes injected into\n"); |
| fprintf( |
| stderr, |
| " -listall\t\tlist all processes on the system, show whether injected_into\n"); |
| fprintf(stderr, |
| " -listdlls\t\tlist all DLLs [short] for a specific pid or executable\n"); |
| fprintf(stderr, |
| " -showdlls\t\tlist all DLLs [long] for a specific pid or executable\n"); |
| fprintf( |
| stderr, |
| " -nopid\t\t\tdoes not display PIDs of processes (useful for expect files)\n"); |
| #ifdef X64 |
| fprintf(stderr, " -no32\t\t\tdoes not display whether 32-bit\n"); |
| #endif |
| fprintf(stderr, " -onlypid\t\tonly shows PID\n"); |
| fprintf(stderr, " -out file\t\toutput to file instead of stdout\n"); |
| fprintf(stderr, " -cmdline\t\tshow process command lines\n"); |
| fprintf(stderr, " -showmem\t\tshow memory stats\n"); |
| fprintf(stderr, |
| " -showtime\t\tshow scheduled time for each process (needs -showmem)\n"); |
| fprintf(stderr, " -showstats\t\tshow internal stats\n"); |
| fprintf(stderr, |
| " -nobuildnum\t\tdoes not display build number of SC (useful for expect " |
| "files)\n"); |
| fprintf(stderr, " -qname strip\t\tshow qualified names; set strip to 0 or 1\n"); |
| fprintf(stderr, " -noqnames\t\tdon\'t show qualified names\n"); |
| fprintf(stderr, " -hot_patch\t\tshow hot patch status\n"); |
| fprintf(stderr, |
| " -s n\t\t\tsample every n millis (default: 500ms, e.g. 1s=1000, 5min=300000 " |
| "5*60*1000)\n"); |
| fprintf(stderr, " -tillidle\t\tsample until idle (default: < 3%% cpu for 3s)\n"); |
| fprintf(stderr, |
| " -idlecpu c\t\tconsider < c%% cpu utilization idle (default: 3%%)\n"); |
| /* Sometimes -listall -showmem causes lsass %cpu to remain high, so |
| * we provide an option to sample frequently but query lsass less frequently. |
| */ |
| fprintf(stderr, |
| " -showmemfreq f\t\tfor -tillidle -showmem, show -showmem every f samples " |
| "(default: 1)\n"); |
| fprintf(stderr, |
| " -idleafter s\t\tflag machine is idle after s seconds (default: 3s)\n"); |
| fprintf(stderr, " -v\t\t\tdisplay version information\n\n"); |
| |
| exit(1); |
| } |
| |
| BOOL listdr = FALSE, listall = FALSE, nopid = FALSE, no32 = FALSE, onlypid = FALSE, |
| listdlls = FALSE, showdlls = FALSE, qname = FALSE, strip = FALSE, noqnames = FALSE; |
| cmdline = FALSE, hotp = FALSE, showtime = FALSE, sampling = FALSE, tillidle = FALSE, |
| idle = FALSE, skip = FALSE, showmem = FALSE, showbuild = TRUE, |
| showstats = FALSE; |
| uint pid = 0; |
| uint millis = 0; |
| uint idlecpu = 0; |
| uint showmemfreq = 1; |
| uint flag_after_ms = 0; |
| char *exe = NULL, *outf = NULL; |
| process_id_t save_pid = 0; |
| WCHAR save_module[MAX_PATH]; |
| WCHAR wexe[MAX_PATH]; |
| FILE *fp; |
| LONGLONG total_user = 0; |
| LONGLONG total_kernel = 0; |
| |
| #define MAX_CMDLINE 2048 |
| |
| #define BUFFER_SIZE_BYTES(buf) sizeof(buf) |
| #define BUFFER_SIZE_ELEMENTS(buf) (BUFFER_SIZE_BYTES(buf) / sizeof(buf[0])) |
| #define BUFFER_LAST_ELEMENT(buf) buf[BUFFER_SIZE_ELEMENTS(buf) - 1] |
| #define NULL_TERMINATE_BUFFER(buf) BUFFER_LAST_ELEMENT(buf) = 0 |
| #define NULL_TERMINATE_SIZED_BUFFER(buf, size) (buf)[(size)-1] = 0 |
| |
| static void |
| generate_process_name(process_info_t *pi, WCHAR *name_buf /* OUT */, |
| uint name_buf_length /* elements */) |
| { |
| WCHAR qual_name[MAX_CMDLINE]; |
| WCHAR qual_args[MAX_CMDLINE]; |
| int res; |
| BOOL use_args = FALSE; |
| |
| /* hack: we assume only need qualified names for these hardcoded apps |
| * FIXME: read registry to see whether need qualification |
| */ |
| if (wcsicmp(pi->ProcessName, L"svchost.exe") == 0 || |
| wcsicmp(pi->ProcessName, L"msiexec.exe") == 0 || |
| wcsicmp(pi->ProcessName, L"tomcat.exe") == 0 || |
| wcsicmp(pi->ProcessName, L"dllhost.exe") == 0) { |
| /* use qualified names */ |
| res = get_process_cmdline(pi->ProcessID, qual_name, |
| BUFFER_SIZE_ELEMENTS(qual_name)); |
| NULL_TERMINATE_BUFFER(qual_name); |
| if (res == ERROR_SUCCESS) { |
| if (get_commandline_qualifier( |
| qual_name, qual_args, BUFFER_SIZE_ELEMENTS(qual_args), |
| /* hack: we assume we only strip svchost, |
| * and we also strip dllhost here to |
| * fit more of the GUI in and avoid the |
| * "Processid" string. |
| * FIXME: read from registry. |
| */ |
| (wcsicmp(pi->ProcessName, L"svchost.exe") != 0 && |
| wcsicmp(pi->ProcessName, L"dllhost.exe") != 0))) { |
| NULL_TERMINATE_BUFFER(qual_args); |
| use_args = TRUE; |
| } /* else not an error, just no args. e.g.: plain sqlservr.exe */ |
| } else { |
| /* this is an error => notify user */ |
| _snwprintf(qual_args, BUFFER_SIZE_ELEMENTS(qual_args), L"<error>"); |
| NULL_TERMINATE_BUFFER(qual_args); |
| use_args = TRUE; |
| } |
| } |
| if (use_args) { |
| _snwprintf(name_buf, name_buf_length, L"%s-%s", pi->ProcessName, qual_args); |
| } else { |
| _snwprintf(name_buf, name_buf_length, L"%s", pi->ProcessName); |
| } |
| NULL_TERMINATE_SIZED_BUFFER(name_buf, name_buf_length); |
| } |
| |
| static void |
| print_mem_stats(process_info_t *pi, char reschar, int version) |
| { |
| WCHAR qual_name[MAX_CMDLINE]; |
| int cpu = -1, user = -1; |
| LONGLONG wallclock_time = get_system_time() - pi->CreateTime.QuadPart; |
| LONGLONG scheduled_time = pi->UserTime.QuadPart + pi->KernelTime.QuadPart; |
| static LONGLONG firstproc_time = 0; |
| if (wallclock_time != (LONGLONG)0) { |
| cpu = (int)((100 * scheduled_time) / wallclock_time); |
| } |
| if (scheduled_time != (LONGLONG)0) { |
| user = (int)((100 * pi->UserTime.QuadPart) / scheduled_time); |
| } |
| |
| /* Total user and kernel time scheduled for all processes. Don't include idle |
| * process, and if -skip option is specified don't include DRview.exe */ |
| if (wcsicmp(pi->ProcessName, L"") != 0 && |
| (wcsicmp(pi->ProcessName, L"drview.exe") != 0 || !skip)) { |
| total_user += pi->UserTime.QuadPart; |
| total_kernel += pi->KernelTime.QuadPart; |
| } |
| |
| /* CreateTime.QuadPart is a counter since 1916. Both idle process and |
| * System have create time of 0, so report create time, in ms, relative to |
| * smss.exe */ |
| if (wcsicmp(pi->ProcessName, L"") == 0 || wcsicmp(pi->ProcessName, L"system") == 0 || |
| wcsicmp(pi->ProcessName, L"smss.exe") == 0) { |
| firstproc_time = pi->CreateTime.QuadPart; |
| } |
| |
| generate_process_name(pi, qual_name, BUFFER_SIZE_ELEMENTS(qual_name)); |
| |
| /* single line best so can line up columns and process output easily */ |
| fprintf(fp, |
| "%-23.23S %5d %c %5d %2d%% %3d%% %5d %3d %7zd %7zd %8zd %7zd %7zd %7zd %7d " |
| "%5zd %5zd " |
| "%5zd %5zd %5d", |
| qual_name, pi->ProcessID, reschar, version, cpu, user, pi->HandleCount, |
| pi->ThreadCount, pi->VmCounters.PeakVirtualSize / 1024, |
| pi->VmCounters.VirtualSize / 1024, pi->VmCounters.PeakPagefileUsage / 1024, |
| pi->VmCounters.PagefileUsage / 1024, /* aka Private */ |
| pi->VmCounters.PeakWorkingSetSize / 1024, |
| pi->VmCounters.WorkingSetSize / 1024, pi->VmCounters.PageFaultCount, |
| pi->VmCounters.QuotaPeakPagedPoolUsage / 1024, |
| pi->VmCounters.QuotaPagedPoolUsage / 1024, |
| pi->VmCounters.QuotaPeakNonPagedPoolUsage / 1024, |
| pi->VmCounters.QuotaNonPagedPoolUsage / 1024, pi->InheritedFromProcessID); |
| if (showtime) { |
| /* QuadPart is in ticks 1 tick = 100 nano secs. In ms = n * 100/(1000*1000) */ |
| fprintf(fp, " %15I64d %15I64d %15I64d", (pi->UserTime.QuadPart / 10000), |
| (pi->KernelTime.QuadPart / 10000), |
| ((pi->CreateTime.QuadPart - firstproc_time) / 10000)); |
| } |
| fprintf(fp, "\n"); |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| int nprocs, argidx = 1; |
| |
| if (argc < 2) |
| usage(); |
| |
| while (argidx < argc) { |
| |
| if (!strcmp(argv[argidx], "-help")) { |
| help(); |
| } else if (!strcmp(argv[argidx], "-pid") || !strcmp(argv[argidx], "-p")) { |
| if (argidx + 1 >= argc) |
| usage(); |
| pid = atoi(argv[++argidx]); |
| } else if (!strcmp(argv[argidx], "-exe")) { |
| if (argidx + 1 >= argc) |
| usage(); |
| exe = argv[++argidx]; |
| _snwprintf(wexe, MAX_PATH, L"%S", exe); |
| wexe[MAX_PATH - 1] = L'\0'; |
| } else if (!strcmp(argv[argidx], "-listall")) { |
| listall = TRUE; |
| } else if (!strcmp(argv[argidx], "-listdr")) { |
| listdr = TRUE; |
| } else if (!strcmp(argv[argidx], "-listdlls")) { |
| listdlls = TRUE; |
| } else if (!strcmp(argv[argidx], "-showdlls")) { |
| listdlls = TRUE; |
| showdlls = TRUE; |
| } else if (!strcmp(argv[argidx], "-nopid")) { |
| nopid = TRUE; |
| } else if (!strcmp(argv[argidx], "-no32")) { |
| no32 = TRUE; |
| } else if (!strcmp(argv[argidx], "-onlypid")) { |
| onlypid = TRUE; |
| } else if (!strcmp(argv[argidx], "-out")) { |
| if (argidx + 1 >= argc) |
| usage(); |
| outf = argv[++argidx]; |
| } else if (!strcmp(argv[argidx], "-cmdline")) { |
| cmdline = TRUE; |
| } else if (!strcmp(argv[argidx], "-showstats")) { |
| showstats = TRUE; |
| } else if (!strcmp(argv[argidx], "-showmem")) { |
| showmem = TRUE; |
| } else if (!strcmp(argv[argidx], "-showtime")) { |
| if (!showmem) |
| usage(); |
| showtime = TRUE; |
| } else if (!strcmp(argv[argidx], "-nobuildnum")) { |
| showbuild = FALSE; |
| } else if (!strcmp(argv[argidx], "-qname")) { |
| if (argidx + 1 >= argc) |
| usage(); |
| qname = TRUE; |
| strip = atoi(argv[++argidx]); |
| } else if (!strcmp(argv[argidx], "-noqnames")) { |
| noqnames = TRUE; |
| } else if (!strcmp(argv[argidx], "-hot_patch")) { |
| hotp = TRUE; |
| } else if (!strcmp(argv[argidx], "-s")) { |
| millis = (uint)atoi(argv[argidx + 1]); |
| if (millis == 0) { |
| millis = 500; |
| } else { |
| argidx++; |
| } |
| sampling = TRUE; |
| } else if (!strcmp(argv[argidx], "-tillidle")) { |
| millis = (millis <= 0) ? 500 : millis; |
| idlecpu = (idlecpu <= 0) ? 3 : idlecpu; |
| showmemfreq = (showmemfreq <= 1) ? 1 : showmemfreq; |
| flag_after_ms = (flag_after_ms <= 0) ? 3000 : flag_after_ms; |
| tillidle = TRUE; |
| sampling = TRUE; |
| } else if (!strcmp(argv[argidx], "-idlecpu")) { |
| if (argidx + 1 >= argc) |
| usage(); |
| idlecpu = (uint)atoi(argv[++argidx]); |
| idlecpu = (idlecpu <= 0) ? 3 : idlecpu; |
| millis = (millis <= 0) ? 500 : millis; |
| flag_after_ms = (flag_after_ms <= 0) ? 3000 : flag_after_ms; |
| tillidle = TRUE; |
| sampling = TRUE; |
| } else if (!strcmp(argv[argidx], "-showmemfreq")) { |
| if (argidx + 1 >= argc) |
| usage(); |
| showmemfreq = (uint)atoi(argv[++argidx]); |
| showmemfreq = (showmemfreq <= 1) ? 1 : showmemfreq; |
| showmem = TRUE; |
| tillidle = TRUE; |
| sampling = TRUE; |
| } else if (!strcmp(argv[argidx], "-idleafter")) { |
| if (argidx + 1 >= argc) |
| usage(); |
| flag_after_ms = (uint)atoi(argv[++argidx]); |
| flag_after_ms = (flag_after_ms <= 0) ? 3000 : flag_after_ms; |
| idlecpu = (idlecpu <= 0) ? 3 : idlecpu; |
| millis = (millis <= 0) ? 500 : millis; |
| sampling = TRUE; |
| tillidle = TRUE; |
| } else if (!strcmp(argv[argidx], "-skip")) { |
| /* internal option: skip drview's contribution to total scheduled time */ |
| skip = TRUE; |
| } else if (!strcmp(argv[argidx], "-v")) { |
| #if defined(BUILD_NUMBER) && defined(VERSION_NUMBER) |
| printf("drview.exe version %s -- build %d\n", STRINGIFY(VERSION_NUMBER), |
| BUILD_NUMBER); |
| #elif defined(BUILD_NUMBER) |
| printf("drview.exe custom build %d -- %s\n", BUILD_NUMBER, __DATE__); |
| #else |
| printf("drview.exe custom build -- %s, %s\n", __DATE__, __TIME__); |
| #endif |
| exit(0); |
| } else { |
| fprintf(stderr, "Unknown option: %s\n", argv[argidx]); |
| usage(); |
| } |
| argidx++; |
| } |
| |
| if (listdlls && !pid && !exe && !listall && !listdr) { |
| fprintf(stderr, |
| "-listdlls option should be combined with a specific -pid, -exe, -listdr " |
| "or -listall option\n"); |
| usage(); |
| } |
| |
| if (outf) { |
| fp = fopen(outf, "w"); |
| if (fp == NULL) { |
| fprintf(stderr, "Error opening %s for output.\n", outf); |
| exit(-1); |
| } |
| } else |
| fp = stdout; |
| |
| /* Invoke DLL routine when -listdlls combined with either -listall or -pid or -exe */ |
| if (listdlls && (listall || pid || exe || listdr)) { |
| dllwalk(); |
| } else { |
| do { |
| nprocs = procwalk(); |
| fflush(fp); |
| if (sampling) |
| Sleep(millis); |
| /* FIXME: looping infinitely, make sure we are not leaking anything */ |
| } while (sampling && !idle); |
| } |
| |
| if (outf) |
| fclose(fp); |
| |
| return nprocs; |
| } |
| |
| int count; |
| |
| /* returns FALSE if status is effectively disabled, otherwise returns |
| * TRUE (indicating status should be displayed. */ |
| BOOL |
| get_status_string(char *buf, UINT maxchars, hotp_inject_status_t status, |
| hotp_policy_mode_t mode) |
| { |
| char *statptr = NULL; |
| char *modeptr = NULL; |
| |
| if (status == HOTP_INJECT_NO_MATCH && mode == HOTP_MODE_OFF) |
| return FALSE; |
| |
| switch (status) { |
| case HOTP_INJECT_ERROR: statptr = "Inject Error!"; break; |
| case HOTP_INJECT_PROTECT: statptr = "Injected Protector"; break; |
| case HOTP_INJECT_DETECT: statptr = "Injected Detector"; break; |
| case HOTP_INJECT_IN_PROGRESS: statptr = "Injection in progress"; break; |
| case HOTP_INJECT_PENDING: statptr = "Inject point not yet executed"; break; |
| case HOTP_INJECT_NO_MATCH: statptr = "Not matched"; break; |
| case HOTP_INJECT_OFF: |
| /* not currently used. */ |
| default: statptr = NULL; |
| } |
| |
| switch (mode) { |
| case HOTP_MODE_OFF: modeptr = "Off"; break; |
| case HOTP_MODE_DETECT: modeptr = "Detect"; break; |
| case HOTP_MODE_PROTECT: modeptr = "Protect"; break; |
| default: modeptr = NULL; |
| } |
| |
| if (statptr == NULL || modeptr == NULL) { |
| _snprintf(buf, maxchars, "[Status ERROR: Status=%d, Mode=%d]", status, mode); |
| } else { |
| _snprintf(buf, maxchars, "%s [%s]", modeptr, statptr); |
| } |
| |
| buf[maxchars - 1] = '\0'; |
| |
| return TRUE; |
| } |
| |
| BOOL |
| pw_callback(process_info_t *pi, void **param) |
| { |
| char *resstr; |
| char reschar; |
| int res; |
| WCHAR buf[MAX_CMDLINE]; |
| DWORD version; |
| BOOL under_dr; |
| |
| WCHAR qual_name[MAX_CMDLINE]; |
| if (exe) |
| generate_process_name(pi, qual_name, BUFFER_SIZE_ELEMENTS(qual_name)); |
| |
| if ((pid && pi->ProcessID == pid) || |
| (exe && (!wcsicmp(wexe, pi->ProcessName) || !wcsicmp(wexe, qual_name))) || |
| listall || listdr) { |
| version = -1; |
| res = under_dynamorio_ex(pi->ProcessID, &version); |
| switch (res) { |
| case DLL_PROFILE: |
| resstr = NAME " profile"; |
| reschar = 'P'; |
| break; |
| case DLL_RELEASE: |
| resstr = NAME " release"; |
| reschar = 'R'; |
| break; |
| case DLL_DEBUG: |
| resstr = NAME " debug"; |
| reschar = 'D'; |
| break; |
| case DLL_CUSTOM: |
| resstr = NAME " custom"; |
| reschar = 'C'; |
| break; |
| case DLL_NONE: |
| resstr = "native"; |
| reschar = 'N'; |
| break; |
| case DLL_UNKNOWN: |
| default: resstr = "<error>"; reschar = '?'; |
| } |
| |
| under_dr = !(res == DLL_NONE || res == DLL_UNKNOWN); |
| |
| if (!listdr || under_dr) { |
| if (!nopid && !showmem) { |
| if (onlypid) |
| fprintf(fp, "%d\n", (DWORD)pi->ProcessID); |
| else |
| fprintf(fp, "PID %d, ", (DWORD)pi->ProcessID); |
| } |
| if (!showmem && !onlypid) { |
| WCHAR qual_name[MAX_CMDLINE]; |
| WCHAR *name_to_use = pi->ProcessName; |
| #ifdef X64 |
| HANDLE hproc = |
| OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pi->ProcessID); |
| if (is_wow64(hproc)) { |
| if (!no32) |
| fprintf(fp, "32-bit, "); |
| /* FIXME: currently x64 process can't see 32-bit |
| * drmarker |
| */ |
| resstr = "<unknown>"; |
| } |
| CloseHandle(hproc); |
| #endif |
| if (!noqnames) { |
| generate_process_name(pi, qual_name, BUFFER_SIZE_ELEMENTS(qual_name)); |
| name_to_use = qual_name; |
| } |
| fprintf(fp, "Process %S, ", name_to_use); |
| if (version == -1 || !showbuild) |
| fprintf(fp, "running %s\n", resstr); |
| else |
| fprintf(fp, "running %s (build %d)\n", resstr, version); |
| } |
| if (cmdline) { |
| res = get_process_cmdline(pi->ProcessID, buf, BUFFER_SIZE_ELEMENTS(buf)); |
| NULL_TERMINATE_BUFFER(buf); |
| if (res == ERROR_SUCCESS) { |
| fprintf(fp, "\tCmdline: %S\n", buf); |
| } else { |
| /* acquiring SeDebugPrivilege requires being admin */ |
| if (res == ERROR_NOT_ALL_ASSIGNED) |
| fprintf(fp, "\t<Re-run as administrator for cmdline>\n"); |
| else |
| fprintf(fp, "\t<Cmdline err %d>\n", res); |
| } |
| } |
| if (qname) { |
| WCHAR cmdline[MAX_CMDLINE]; |
| res = get_process_cmdline(pi->ProcessID, cmdline, |
| BUFFER_SIZE_ELEMENTS(cmdline)); |
| NULL_TERMINATE_BUFFER(cmdline); |
| if (res == ERROR_SUCCESS) { |
| if (!get_commandline_qualifier(cmdline, buf, |
| BUFFER_SIZE_ELEMENTS(buf), !strip)) |
| buf[0] = L'\0'; /* no args */ |
| NULL_TERMINATE_BUFFER(buf); |
| } |
| if (res == ERROR_SUCCESS) |
| fprintf(fp, "\tQname: %S%s%S\n", pi->ProcessName, |
| buf[0] == L'\0' ? "" : "-", buf); |
| else |
| fprintf(fp, "\t<Qname err %d>\n", res); |
| } |
| if (under_dr && hotp) { |
| hotp_policy_status_table_t *status_tbl = NULL; |
| res = get_hotp_status(pi->ProcessID, &status_tbl); |
| if (res == ERROR_SUCCESS) { |
| uint j; |
| hotp_policy_status_t *cur; |
| fprintf(fp, "\tHotpatching:\n"); |
| for (j = 0; j < status_tbl->num_policies; j++) { |
| char status_buf[MAX_PATH]; |
| cur = &(status_tbl->policy_status_array[j]); |
| if (get_status_string(status_buf, MAX_PATH, cur->inject_status, |
| cur->mode)) |
| fprintf(fp, "\t Patch %s: %s\n", cur->policy_id, status_buf); |
| } |
| } else if (res == ERROR_DRMARKER_ERROR) { |
| fprintf(fp, "\tHot Patching Not Enabled\n"); |
| } else { |
| fprintf(fp, "\t<Hotpatch Query Error %d>\n", res); |
| } |
| } |
| if (under_dr && showstats) { |
| dr_statistics_t *stats = get_dynamorio_stats(pi->ProcessID); |
| if (stats != NULL) { |
| uint i; |
| fprintf(fp, "\t%.*s\n", |
| (int)BUFFER_SIZE_ELEMENTS(stats->process_name), |
| stats->process_name); |
| for (i = 0; i < stats->num_stats; i++) { |
| fprintf(fp, "\t%*.*s :%9zd\n", |
| (int)BUFFER_SIZE_ELEMENTS(stats->stats[i].name), |
| (int)BUFFER_SIZE_ELEMENTS(stats->stats[i].name), |
| stats->stats[i].name, stats->stats[i].value); |
| } |
| } |
| free_dynamorio_stats(stats); |
| } |
| if (showmem) { |
| print_mem_stats(pi, reschar, version); |
| } |
| count++; |
| } |
| } |
| return TRUE; |
| } |
| |
| BOOL |
| dllw_callback(module_info_t *mi, void **param) |
| { |
| char *resstr; |
| char reschar; |
| int res; |
| DWORD version; |
| BOOL exe_match = (exe && wcsstr(mi->BaseDllName, wexe)) ? TRUE : FALSE; |
| BOOL exe_present = |
| (wcsstr(mi->BaseDllName, L".exe") || wcsstr(mi->BaseDllName, L".EXE")) ? TRUE |
| : FALSE; |
| |
| /* -listdlls option is set && any of below 3 conditions hold true. |
| (1) -pid value is set and current threadID matches. |
| (2) -exe is set and all DLLs with matching threadIDs (a fake comparison) since |
| WINDOWS provides no other handle. (3) -listall option is set |
| */ |
| |
| if (listdlls && |
| ((pid && mi->ProcessID == pid) || |
| ((exe_match || |
| (exe && save_pid == mi->ProcessID && |
| (!exe_present || wcscmp(save_module, mi->BaseDllName) == 0)))) || |
| (listall || listdr))) { |
| version = -1; |
| res = under_dynamorio_ex(mi->ProcessID, &version); |
| switch (res) { |
| case DLL_PROFILE: |
| resstr = NAME " profile"; |
| reschar = 'P'; |
| break; |
| case DLL_RELEASE: |
| resstr = NAME " release"; |
| reschar = 'R'; |
| break; |
| case DLL_DEBUG: |
| resstr = NAME " debug"; |
| reschar = 'D'; |
| break; |
| case DLL_CUSTOM: |
| resstr = NAME " custom"; |
| reschar = 'C'; |
| break; |
| case DLL_NONE: |
| resstr = "native"; |
| reschar = 'N'; |
| break; |
| case DLL_UNKNOWN: |
| default: resstr = "<error>"; reschar = '?'; |
| } |
| |
| save_pid = mi->ProcessID; |
| |
| if (!(listdr && (res == DLL_NONE || res == DLL_UNKNOWN))) { |
| if (wcsstr(mi->BaseDllName, L".exe") || wcsstr(mi->BaseDllName, L".EXE")) { |
| wcsncpy(save_module, mi->BaseDllName, |
| sizeof(save_module) / sizeof(save_module[0])); |
| save_module[(sizeof(save_module) / sizeof(save_module[0])) - 1] = L'\0'; |
| if (!nopid && !showmem) |
| fprintf(fp, "\n\nPID " PIDFMT, mi->ProcessID); |
| if (!showmem) { |
| if (version == -1 || !showbuild) { |
| fprintf(fp, "\t\tProcess %S, running %s\n", mi->BaseDllName, |
| resstr); |
| } else { |
| fprintf(fp, "\t\tProcess %S, running %s (build %d)\n", |
| mi->BaseDllName, resstr, version); |
| } |
| } |
| count = 0; |
| } else { |
| if (!showmem) { |
| if (showdlls) { |
| /* long format */ |
| fprintf(fp, " %p-%p %-16S Stamp=%x Count=%d\n %S\n", |
| mi->BaseAddress, |
| (char *)mi->BaseAddress + mi->SizeOfImage, |
| mi->BaseDllName, mi->TimeDateStamp, mi->LoadCount, |
| mi->FullDllName); |
| } else { |
| fprintf(fp, "\t%-16S", mi->BaseDllName); |
| count++; |
| if ((count % 3) == 0) { |
| fprintf(fp, "\n"); |
| count = 0; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| int |
| procwalk() |
| { |
| static int pwalk_per = 0; |
| uint system_load = get_system_load(sampling); |
| if (tillidle) { |
| static uint idle_for_ms = 0; |
| idle_for_ms = (system_load < idlecpu) ? idle_for_ms + millis : 0; |
| idle = (idle_for_ms >= flag_after_ms); |
| } |
| |
| count = 0; |
| if (showmem) { |
| SYSTEM_PERFORMANCE_INFORMATION sperf_info; |
| if (get_system_performance_info(&sperf_info)) { |
| fprintf(fp, "System committed memory (KB): %d / %d peak %d\n", |
| /* in pages so x4==KB */ |
| 4 * sperf_info.TotalCommittedPages, 4 * sperf_info.TotalCommitLimit, |
| 4 * sperf_info.PeakCommitment); |
| /* FIXME: add physical memory, kernel memory, etc. */ |
| } |
| fprintf(fp, "System load: %d%%\t\tUptime: %lu ms\n", system_load, get_uptime()); |
| |
| /* %user reaches 100% so we give it the extra column over %cpu */ |
| fprintf(fp, |
| "%-23s %5s %7s %3s %4s %5s %3s %7s %7s %8s %7s %7s %7s %7s %5s %5s %5s " |
| "%5s %5s", |
| "Name-Qualification", "PID", "DR Bld", "CPU", "User", "Hndl", "Thr", |
| "PVSz", "VSz", "PPriv", "Priv", "PWSS", "WSS", "Fault", "PPage", "Page", |
| "PNonP", "NonP", "PPID"); |
| if (showtime) |
| fprintf(fp, " %15s %15s %15s", "User Time(ms)", "Kernel Time(ms)", |
| "Create Time(ms)"); |
| fprintf(fp, "\n"); |
| } |
| if (pwalk_per == 0 || pwalk_per % showmemfreq == 0) { |
| process_walk(&pw_callback, NULL); |
| if (!count) |
| fprintf(fp, "No such process found.\n"); |
| else { |
| if (showmem && showtime) { |
| fprintf(fp, "Total scheduled user/kernel time: %I64d/%I64d ms\n", |
| total_user / 10000, total_kernel / 10000); |
| } |
| } |
| } |
| pwalk_per++; |
| |
| total_user = 0; |
| total_kernel = 0; |
| |
| return count; |
| } |
| |
| void |
| dllwalk() |
| { |
| count = 0; |
| dll_walk_all(&dllw_callback, NULL); |
| } |