| /* ********************************************************** |
| * Copyright (c) 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. |
| */ |
| |
| /* Copyright (c) 2001-2002 Massachusetts Institute of Technology |
| * |
| * This file is part of RIO, a research tool that is being supplied |
| * "as is" without any accompanying services or improvements from MIT. |
| * MIT makes no representations or warranties, express or implied. |
| * Permission is hereby granted for non-commercial use of RIO provided |
| * that you preserve this copyright notice, and you do not mention |
| * the copyright holders in advertising related to RIO without |
| * their permission. |
| */ |
| /* |
| * winstats.c -- runs a target executable and gives memory stats at completion |
| * build like so: cl /nologo winstats.c user32.lib imagehlp.lib |
| */ |
| |
| /* FIXME: Unicode support?!?! */ |
| |
| /* xref PR 231843, we target 2003 to get a version of PROCESS_ALL_ACCESS that |
| * works for all platforms. */ |
| #define _WIN32_WINNT _WIN32_WINNT_WS03 |
| |
| #include <windows.h> |
| #include <commdlg.h> |
| #include <imagehlp.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <time.h> |
| #include <tchar.h> |
| |
| #include "configure.h" |
| #include "globals_shared.h" |
| |
| #include "ntdll.h" |
| |
| /* global vars */ |
| static int limit; /* in seconds */ |
| static BOOL showmem; |
| static double wallclock; /* in seconds */ |
| |
| #define FP stderr |
| |
| static void |
| print_mem_stats(HANDLE h) |
| { |
| long secs = (long)wallclock; |
| VM_COUNTERS mem; |
| int cpu; |
| if (!get_process_load_ex(h, &cpu, NULL)) |
| cpu = -1; |
| get_process_mem_stats(h, &mem); |
| #if VERBOSE |
| printf("Process Memory Statistics:\n"); |
| printf("\tPeak virtual size: %6d KB\n", mem.PeakVirtualSize / 1024); |
| printf("\tPeak working set size: %6d KB\n", mem.PeakWorkingSetSize / 1024); |
| printf("\tPeak paged pool usage: %6d KB\n", mem.QuotaPeakPagedPoolUsage / 1024); |
| printf("\tPeak non-paged pool usage: %6d KB\n", |
| mem.QuotaPeakNonPagedPoolUsage / 1024); |
| printf("\tPeak pagefile usage: %6d KB\n", mem.PeakPagefileUsage / 1024); |
| #endif |
| /* Elapsed real (wall clock) time. */ |
| if (secs >= 3600) { /* One hour -> h:m:s. */ |
| fprintf(FP, "%ld:%02ld:%02ldelapsed ", secs / 3600, (secs % 3600) / 60, |
| secs % 60); |
| } else { |
| fprintf(FP, "%ld:%02ld.%02ldelapsed ", /* -> m:s. */ |
| secs / 60, secs % 60, 0 /* for now */); |
| } |
| fprintf(FP, "%d%%CPU \n", cpu); |
| fprintf(FP, "(%zu tot, %zu RSS, %zu paged, %zu non, %zu swap)k\n", |
| mem.PeakVirtualSize / 1024, mem.PeakWorkingSetSize / 1024, |
| mem.QuotaPeakPagedPoolUsage / 1024, mem.QuotaPeakNonPagedPoolUsage / 1024, |
| mem.PeakPagefileUsage / 1024); |
| } |
| |
| #define DEBUGPRINT 0 |
| |
| /* FIXME: would like ^C to kill child process, it doesn't. |
| * also, child process seems able to read stdin but not to write |
| * to stdout or stderr (in fact it dies if it tries) |
| */ |
| #define HANDLE_CONTROL_C 0 |
| |
| static void |
| debugbox(char *msg) |
| { |
| MessageBox(NULL, TEXT(msg), TEXT("Inject Progress"), MB_OK | MB_TOPMOST); |
| } |
| |
| #if HANDLE_CONTROL_C |
| BOOL WINAPI |
| HandlerRoutine(DWORD dwCtrlType // control signal type |
| ) |
| { |
| printf("Inside HandlerRoutine!\n"); |
| fflush(stdout); |
| /* GenerateConsoleCtrlEvent(dwCtrlType, phandle);*/ |
| return TRUE; |
| } |
| #endif |
| |
| int |
| usage(char *us) |
| { |
| printf("Usage: %s [-s limit_sec | -m limit_min | -h limit_hr]\n" |
| " [-silent] <program> <args...>\n", |
| us); |
| return 0; |
| } |
| |
| #define MAX_CMDLINE 2048 |
| |
| /* we must be a console application in order to have the process |
| * we launch NOT get a brand new console window! |
| */ |
| int __cdecl main(int argc, char *argv[], char *envp[]) |
| { |
| LPTSTR app_name = NULL; |
| TCHAR app_cmdline[MAX_CMDLINE]; |
| LPTSTR ch; |
| int i; |
| LPTSTR pszCmdLine = GetCommandLine(); |
| PLOADED_IMAGE li; |
| STARTUPINFO si; |
| PROCESS_INFORMATION pi; |
| HANDLE phandle; |
| BOOL success; |
| DWORD wait_result; |
| int arg_offs = 1; |
| time_t start_time, end_time; |
| |
| STARTUPINFO mysi; |
| GetStartupInfo(&mysi); |
| |
| /* parse args */ |
| limit = 0; |
| showmem = TRUE; |
| if (argc < 2) { |
| return usage(argv[0]); |
| } |
| while (argv[arg_offs][0] == '-') { |
| if (strcmp(argv[arg_offs], "-s") == 0) { |
| if (argc <= arg_offs + 1) |
| return usage(argv[0]); |
| limit = atoi(argv[arg_offs + 1]); |
| arg_offs += 2; |
| } else if (strcmp(argv[arg_offs], "-m") == 0) { |
| if (argc <= arg_offs + 1) |
| return usage(argv[0]); |
| limit = atoi(argv[arg_offs + 1]) * 60; |
| arg_offs += 2; |
| } else if (strcmp(argv[arg_offs], "-h") == 0) { |
| if (argc <= arg_offs + 1) |
| return usage(argv[0]); |
| limit = atoi(argv[arg_offs + 1]) * 3600; |
| arg_offs += 2; |
| } else if (strcmp(argv[arg_offs], "-v") == 0) { |
| /* just ignore -v, only here for compatibility with texec */ |
| arg_offs += 1; |
| } else if (strcmp(argv[arg_offs], "-silent") == 0) { |
| showmem = FALSE; |
| arg_offs += 1; |
| } else { |
| return usage(argv[0]); |
| } |
| if (limit < 0) { |
| return usage(argv[0]); |
| } |
| if (argc - arg_offs < 1) { |
| return usage(argv[0]); |
| } |
| } |
| |
| /* we don't want quotes included in our |
| * application path string (app_name), but we do want to put |
| * quotes around every member of the command line |
| */ |
| app_name = argv[arg_offs]; |
| if (app_name[0] == '\"' || app_name[0] == '\'' || app_name[0] == '`') { |
| /* remove quotes */ |
| app_name++; |
| app_name[strlen(app_name) - 1] = '\0'; |
| } |
| /* note that we want target app name as part of cmd line |
| * (FYI: if we were using WinMain, the pzsCmdLine passed in |
| * does not have our own app name in it) |
| * since cygwin shell ends up putting extra quote characters, etc. |
| * in pszCmdLine ('"foo"' => "\"foo\""), we can't easily walk past |
| * the 1st 2 args (our path and the dll path), so we reconstruct |
| * the cmd line from scratch: |
| */ |
| ch = app_cmdline; |
| ch += _snprintf(app_cmdline, MAX_CMDLINE, "\"%s\"", app_name); |
| for (i = arg_offs + 1; i < argc; i++) |
| ch += _snprintf(ch, MAX_CMDLINE - (ch - app_cmdline), " \"%s\"", argv[i]); |
| assert(ch - app_cmdline < MAX_CMDLINE); |
| #if DEBUGPRINT |
| printf("Running \"%s\"\n", app_cmdline); |
| fflush(stdout); |
| #endif |
| |
| #if HANDLE_CONTROL_C |
| if (!SetConsoleCtrlHandler(HandlerRoutine, TRUE)) { |
| debugbox("SetConsoleCtrlHandler failed"); |
| return 1; |
| } |
| { |
| int flags; |
| /* FIXME: hStdInput apparently is not the right handle...how do I get |
| * the right handle?!? |
| */ |
| if (!GetConsoleMode(mysi.hStdInput, &flags)) { |
| debugbox("GetConsoleMode failed"); |
| return 1; |
| } |
| printf("console mode flags are: 0x%08x\n", flags); |
| } |
| #endif |
| |
| if (li = ImageLoad(app_name, NULL)) { |
| ImageUnload(li); |
| } else { |
| _snprintf(app_cmdline, MAX_CMDLINE, "Failed to load executable image \"%s\"", |
| app_name); |
| debugbox(app_cmdline); |
| return 1; |
| } |
| |
| /* Launch the application process. */ |
| ZeroMemory(&si, sizeof(si)); |
| si.cb = sizeof(si); |
| si.dwFlags = STARTF_USESTDHANDLES; |
| si.hStdInput = mysi.hStdInput; |
| si.hStdOutput = mysi.hStdOutput; |
| si.hStdError = mysi.hStdError; |
| |
| /* Must specify TRUE for bInheritHandles so child inherits stdin! */ |
| if (!CreateProcess(app_name, app_cmdline, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, |
| NULL, &si, &pi)) { |
| debugbox("Failed to launch application"); |
| return 1; |
| } |
| |
| if ((phandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId)) == NULL) { |
| debugbox("Cannot open application process"); |
| TerminateProcess(pi.hProcess, 0); |
| return 1; |
| } |
| |
| #if DEBUGPRINT |
| printf("Successful at starting process\n"); |
| fflush(stdout); |
| #endif |
| |
| success = FALSE; |
| __try { |
| start_time = time(NULL); |
| |
| /* resume the suspended app process so its main thread can run */ |
| ResumeThread(pi.hThread); |
| |
| /* detach from the app process */ |
| CloseHandle(pi.hThread); |
| CloseHandle(pi.hProcess); |
| |
| /* now wait for app process to finish */ |
| wait_result = |
| WaitForSingleObject(phandle, (limit == 0) ? INFINITE : (limit * 1000)); |
| end_time = time(NULL); |
| wallclock = difftime(end_time, start_time); |
| if (wait_result == WAIT_OBJECT_0) |
| success = TRUE; |
| else |
| printf("Timeout after %d seconds\n", limit); |
| } __finally { |
| /* FIXME: this is my attempt to get ^C, but it doesn't work... */ |
| #if HANDLE_CONTROL_C |
| if (!success) { |
| printf("Terminating child process!\n"); |
| fflush(stdout); |
| TerminateProcess(phandle, 0); |
| } else |
| printf("Injector exiting peacefully\n"); |
| #endif |
| if (showmem) |
| print_mem_stats(phandle); |
| if (!success) { |
| /* kill the child */ |
| TerminateProcess(phandle, 0); |
| } |
| CloseHandle(phandle); |
| #if DEBUGPRINT |
| fflush(stdout); |
| #endif |
| } |
| |
| return 0; |
| } |