1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/test/test_process_killer_win.h"
6
7 #include <windows.h>
8 #include <winternl.h>
9
10 #include <algorithm>
11
12 #include "base/logging.h"
13 #include "base/process/kill.h"
14 #include "base/process/process_iterator.h"
15 #include "base/strings/string_util.h"
16 #include "base/win/scoped_handle.h"
17
18 namespace {
19
20 typedef LONG WINAPI
21 NtQueryInformationProcess(
22 IN HANDLE ProcessHandle,
23 IN PROCESSINFOCLASS ProcessInformationClass,
24 OUT PVOID ProcessInformation,
25 IN ULONG ProcessInformationLength,
26 OUT PULONG ReturnLength OPTIONAL
27 );
28
29 // Get the function pointer to NtQueryInformationProcess in NTDLL.DLL
GetQIP(NtQueryInformationProcess ** qip_func_ptr)30 static bool GetQIP(NtQueryInformationProcess** qip_func_ptr) {
31 static NtQueryInformationProcess* qip_func =
32 reinterpret_cast<NtQueryInformationProcess*>(
33 GetProcAddress(GetModuleHandle(L"ntdll.dll"),
34 "NtQueryInformationProcess"));
35 DCHECK(qip_func) << "Could not get pointer to NtQueryInformationProcess.";
36 *qip_func_ptr = qip_func;
37 return qip_func != NULL;
38 }
39
40 // Get the command line of a process
GetCommandLineForProcess(uint32 process_id,string16 * cmd_line)41 bool GetCommandLineForProcess(uint32 process_id, string16* cmd_line) {
42 DCHECK(process_id != 0);
43 DCHECK(cmd_line);
44
45 // Open the process
46 base::win::ScopedHandle process_handle(::OpenProcess(
47 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
48 false,
49 process_id));
50 if (!process_handle) {
51 DLOG(ERROR) << "Failed to open process " << process_id << ", last error = "
52 << GetLastError();
53 }
54
55 // Obtain Process Environment Block
56 NtQueryInformationProcess* qip_func = NULL;
57 if (process_handle) {
58 GetQIP(&qip_func);
59 }
60
61 // Read the address of the process params from the peb.
62 DWORD process_params_address = 0;
63 if (qip_func) {
64 PROCESS_BASIC_INFORMATION info = { 0 };
65 // NtQueryInformationProcess returns an NTSTATUS for whom negative values
66 // are negative. Just check for that instead of pulling in DDK macros.
67 if ((qip_func(process_handle.Get(),
68 ProcessBasicInformation,
69 &info,
70 sizeof(info),
71 NULL)) < 0) {
72 DLOG(ERROR) << "Failed to invoke NtQueryProcessInformation, last error = "
73 << GetLastError();
74 } else {
75 BYTE* peb = reinterpret_cast<BYTE*>(info.PebBaseAddress);
76
77 // The process command line parameters are (or were once) located at
78 // the base address of the PEB + 0x10 for 32 bit processes. 64 bit
79 // processes have a different PEB struct as per
80 // http://msdn.microsoft.com/en-us/library/aa813706(VS.85).aspx.
81 // TODO(robertshield): See about doing something about this.
82 SIZE_T bytes_read = 0;
83 if (!::ReadProcessMemory(process_handle.Get(),
84 peb + 0x10,
85 &process_params_address,
86 sizeof(process_params_address),
87 &bytes_read)) {
88 DLOG(ERROR) << "Failed to read process params address, last error = "
89 << GetLastError();
90 }
91 }
92 }
93
94 // Copy all the process parameters into a buffer.
95 bool success = false;
96 string16 buffer;
97 if (process_params_address) {
98 SIZE_T bytes_read;
99 RTL_USER_PROCESS_PARAMETERS params = { 0 };
100 if (!::ReadProcessMemory(process_handle.Get(),
101 reinterpret_cast<void*>(process_params_address),
102 ¶ms,
103 sizeof(params),
104 &bytes_read)) {
105 DLOG(ERROR) << "Failed to read RTL_USER_PROCESS_PARAMETERS, "
106 << "last error = " << GetLastError();
107 } else {
108 // Read the command line parameter
109 const int max_cmd_line_len = std::min(
110 static_cast<int>(params.CommandLine.MaximumLength),
111 4096);
112 buffer.resize(max_cmd_line_len + 1);
113 if (!::ReadProcessMemory(process_handle.Get(),
114 params.CommandLine.Buffer,
115 &buffer[0],
116 max_cmd_line_len,
117 &bytes_read)) {
118 DLOG(ERROR) << "Failed to copy process command line, "
119 << "last error = " << GetLastError();
120 } else {
121 *cmd_line = buffer;
122 success = true;
123 }
124 }
125 }
126
127 return success;
128 }
129
130 // Used to filter processes by process ID.
131 class ArgumentFilter : public base::ProcessFilter {
132 public:
ArgumentFilter(const string16 & argument)133 explicit ArgumentFilter(const string16& argument)
134 : argument_to_find_(argument) {}
135
136 // Returns true to indicate set-inclusion and false otherwise. This method
137 // should not have side-effects and should be idempotent.
Includes(const base::ProcessEntry & entry) const138 virtual bool Includes(const base::ProcessEntry& entry) const {
139 bool found = false;
140 string16 command_line;
141 if (GetCommandLineForProcess(entry.pid(), &command_line)) {
142 string16::const_iterator it =
143 std::search(command_line.begin(),
144 command_line.end(),
145 argument_to_find_.begin(),
146 argument_to_find_.end(),
147 base::CaseInsensitiveCompareASCII<wchar_t>());
148 found = (it != command_line.end());
149 }
150 return found;
151 }
152
153 protected:
154 string16 argument_to_find_;
155 };
156
157 } // namespace
158
159 namespace base {
160
KillAllNamedProcessesWithArgument(const string16 & process_name,const string16 & argument)161 bool KillAllNamedProcessesWithArgument(const string16& process_name,
162 const string16& argument) {
163 ArgumentFilter argument_filter(argument);
164 return base::KillProcesses(process_name, 0, &argument_filter);
165 }
166
167 } // namespace base
168