Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(357)

Side by Side Diff: chrome/common/conflicts/module_watcher_win.cc

Issue 2473783005: [Win] Create ModuleWatcher. (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "chrome/common/conflicts/module_watcher_win.h"
6
7 #include <tlhelp32.h>
8 #include <windows.h>
9 #include <winternl.h> // For UNICODE_STRING.
10
11 #include <memory>
12
13 #include "base/observer_list_threadsafe.h"
14 #include "base/strings/string_piece.h"
15 #include "base/win/scoped_handle.h"
16
17 namespace {
Patrick Monette 2016/11/03 20:18:15 Usually, the unnamed namespace is put inside the f
chrisha 2016/11/04 15:32:42 Done.
18
19 // These structures and functions are documented in MSDN, see
20 // http://msdn.microsoft.com/en-us/library/gg547638(v=vs.85).aspx
21 // there are however no headers or import libraries available in the
22 // Platform SDK.
23 enum {
24 // The DLL was loaded. The NotificationData parameter points to a
25 // LDR_DLL_LOADED_NOTIFICATION_DATA structure.
26 LDR_DLL_NOTIFICATION_REASON_LOADED = 1,
27 // The DLL was unloaded. The NotificationData parameter points to a
28 // LDR_DLL_UNLOADED_NOTIFICATION_DATA structure.
29 LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2,
30 };
31
32 // Structure that is used for module load notifications.
33 typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA {
34 // Reserved.
35 ULONG Flags;
36 // The full path name of the DLL module.
37 PCUNICODE_STRING FullDllName;
38 // The base file name of the DLL module.
39 PCUNICODE_STRING BaseDllName;
40 // A pointer to the base address for the DLL in memory.
41 PVOID DllBase;
42 // The size of the DLL image, in bytes.
43 ULONG SizeOfImage;
44 } LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;
45
46 // Structure that is used for module unload notifications.
47 typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA {
48 // Reserved.
49 ULONG Flags;
50 // The full path name of the DLL module.
51 PCUNICODE_STRING FullDllName;
52 // The base file name of the DLL module.
53 PCUNICODE_STRING BaseDllName;
54 // A pointer to the base address for the DLL in memory.
55 PVOID DllBase;
56 // The size of the DLL image, in bytes.
57 ULONG SizeOfImage;
58 } LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
59
60 // Union that is used for notifications.
61 typedef union _LDR_DLL_NOTIFICATION_DATA {
62 LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
63 LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
64 } LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;
65
66 // Signature of the otification callback function.
67 typedef VOID(CALLBACK* PLDR_DLL_NOTIFICATION_FUNCTION)(
68 ULONG notification_reason,
69 const LDR_DLL_NOTIFICATION_DATA* notification_data,
70 PVOID context);
71
72 // Signatures of the functions used for registering DLL notification callbacks.
73 typedef NTSTATUS(NTAPI* LdrRegisterDllNotificationFunc)(
74 ULONG flags,
75 PLDR_DLL_NOTIFICATION_FUNCTION notification_function,
76 PVOID context,
77 PVOID* cookie);
78 typedef NTSTATUS(NTAPI* LdrUnregisterDllNotificationFunc)(PVOID cookie);
79
80 // Names of the DLL notification registration functions. These are exported by
81 // ntdll.
82 constexpr char kLdrRegisterDllNotification[] = "LdrRegisterDllNotification";
83 constexpr char kLdrUnregisterDllNotification[] = "LdrUnregisterDllNotification";
84
85 // Helper function for converting a UNICODE_STRING to a string piece.
86 base::StringPiece16 ToStringPiece(const UNICODE_STRING* str) {
87 CHECK_NE(static_cast<const UNICODE_STRING*>(NULL), str);
Patrick Monette 2016/11/03 20:18:15 This should probably be a DCHECK. Also s/NULL/null
chrisha 2016/11/04 15:32:42 (This is cut and pasted code from elsewhere, oops.
88 return base::StringPiece16(str->Buffer, str->Length / sizeof(wchar_t));
89 }
90
91 HMODULE GetNtDll() {
92 HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll");
93 CHECK_NE(static_cast<HMODULE>(NULL), ntdll);
Patrick Monette 2016/11/03 20:18:15 Same
chrisha 2016/11/04 15:32:42 Removed this helper function entirely, as the CHEC
94 return ntdll;
95 }
96
97 } // namespace
98
99 namespace conflicts {
100
101 // Static helper class for forwarding system notifications to the ModuleWatcher.
102 // The notifications here will be called on the thread that is doing the DLL
103 // load, which could be on a variety of threads in Chrome, including threads
104 // injected by third parties.
105 class ModuleWatcher::Bridge {
106 public:
107 // This is invoked by the OS. This forwards to OnModuleLoaded or
108 // OnModuleUnloaded depending on |notification_reason|.
109 static void CALLBACK
110 LoaderNotificationCallback(ULONG notification_reason,
111 const LDR_DLL_NOTIFICATION_DATA* notification_data,
112 PVOID context);
113
114 // Invoked by LoaderNotification for LDR_DLL_NOTIFICATION_REASON_LOADED
115 // events.
116 static void OnModuleLoad(const LDR_DLL_LOADED_NOTIFICATION_DATA& load_data,
117 ModuleWatcher* module_watcher);
118
119 // Invoked by LoaderNotification for LDR_DLL_NOTIFICATION_REASON_UNLOADED
120 // events.
121 static void OnModuleUnload(
122 const LDR_DLL_UNLOADED_NOTIFICATION_DATA& unload_data,
123 ModuleWatcher* module_watcher);
124 };
125
126 // static
127 void ModuleWatcher::Bridge::LoaderNotificationCallback(
128 ULONG notification_reason,
129 const LDR_DLL_NOTIFICATION_DATA* notification_data,
130 PVOID context) {
131 ModuleWatcher* module_watcher = reinterpret_cast<ModuleWatcher*>(context);
132 switch (notification_reason) {
133 case LDR_DLL_NOTIFICATION_REASON_LOADED:
134 return OnModuleLoad(notification_data->Loaded, module_watcher);
135
136 case LDR_DLL_NOTIFICATION_REASON_UNLOADED:
137 return OnModuleUnload(notification_data->Unloaded, module_watcher);
138
139 default:
140 NOTREACHED() << "Unknown LDR_DLL_NOTIFICATION_REASON: "
141 << notification_reason;
142 }
143 }
144
145 // static
146 void ModuleWatcher::Bridge::OnModuleLoad(
147 const LDR_DLL_LOADED_NOTIFICATION_DATA& load_data,
148 ModuleWatcher* module_watcher) {
149 ModuleEvent event = {};
Patrick Monette 2016/11/03 20:18:15 All 4 members of ModuleEvent are always initialize
chrisha 2016/11/04 15:32:42 Done.
150 event.event_type = MODULE_LOADED;
151 event.module_path = base::FilePath(ToStringPiece(load_data.FullDllName));
152 event.load_address = load_data.DllBase;
153 event.size = load_data.SizeOfImage;
154 return module_watcher->Notify(event);
155 }
156
157 // static
158 void ModuleWatcher::Bridge::OnModuleUnload(
159 const LDR_DLL_UNLOADED_NOTIFICATION_DATA& unload_data,
160 ModuleWatcher* module_watcher) {
161 ModuleEvent event = {};
162 event.event_type = MODULE_UNLOADED;
163 event.module_path = base::FilePath(ToStringPiece(unload_data.FullDllName));
164 event.load_address = unload_data.DllBase;
165 event.size = unload_data.SizeOfImage;
166 return module_watcher->Notify(event);
167 }
168
169 ModuleWatcher::ModuleWatcher()
Patrick Monette 2016/11/03 20:18:15 I looked at the design doc. Won't each ModuleWatch
chrisha 2016/11/04 15:32:42 Yeah, but I need the thread affinity, as I want th
170 : started_(false), dll_notification_cookie_(nullptr) {
171 observer_list_ = new base::ObserverListThreadSafe<Observer>();
172 }
173
174 ModuleWatcher::~ModuleWatcher() {}
Patrick Monette 2016/11/03 20:18:15 = default;
chrisha 2016/11/04 15:32:42 Always forgetting about that new feature... Done
175
176 std::unique_ptr<ModuleWatcher::Observer> ModuleWatcher::RegisterCallback(
177 const OnModuleEventCallback& callback) {
178 // Create a new Observer. This will register itself with the |observer_list_|.
179 return std::unique_ptr<Observer>(new Observer(callback, this));
180 }
181
182 void ModuleWatcher::Start() {
183 base::AutoLock lock(lock_);
184 DCHECK(!started_);
185 if (!RegisterDllNotificationCallback())
186 return;
187 EnumerateAlreadyLoadedModules();
188 started_ = true;
189 }
190
191 void ModuleWatcher::Stop() {
192 base::AutoLock lock(lock_);
193 DCHECK(started_);
194 UnregisterDllNotificationCallback();
195 }
196
197 bool ModuleWatcher::IsRunning() {
198 base::AutoLock lock(lock_);
199 return started_;
200 }
201
202 bool ModuleWatcher::RegisterDllNotificationCallback() {
Patrick Monette 2016/11/03 20:18:15 lock_.AssertAquired() in all relevant functions?
chrisha 2016/11/04 15:32:42 Done.
203 LdrRegisterDllNotificationFunc reg_fn =
204 reinterpret_cast<LdrRegisterDllNotificationFunc>(
205 ::GetProcAddress(GetNtDll(), kLdrRegisterDllNotification));
206
207 if (reg_fn == NULL)
Patrick Monette 2016/11/03 20:18:15 if (!reg_fn)
chrisha 2016/11/04 15:32:42 Done.
208 return false;
209
210 NTSTATUS status = reg_fn(0, &Bridge::LoaderNotificationCallback, this,
211 &dll_notification_cookie_);
212 return status == 0;
213 }
214
215 bool ModuleWatcher::UnregisterDllNotificationCallback() {
Patrick Monette 2016/11/03 20:18:15 The return value is unused. Not sure if it's worth
chrisha 2016/11/04 15:32:42 This is a thin wrapper around the system function,
216 LdrUnregisterDllNotificationFunc unreg_fn =
217 reinterpret_cast<LdrUnregisterDllNotificationFunc>(
218 ::GetProcAddress(GetNtDll(), kLdrUnregisterDllNotification));
219
220 if (unreg_fn == NULL)
Patrick Monette 2016/11/03 20:18:15 if (!unreg_fn)
chrisha 2016/11/04 15:32:42 Done.
221 return false;
222
223 NTSTATUS status = unreg_fn(dll_notification_cookie_);
224 return status == 0;
225 }
226
227 bool ModuleWatcher::EnumerateAlreadyLoadedModules() {
228 // Get all modules in the current process.
229 base::win::ScopedHandle snap(
230 ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ::GetCurrentProcessId()));
231 if (!snap.Get())
Patrick Monette 2016/11/03 20:18:15 if (!snap.IsValid())
chrisha 2016/11/04 15:32:42 Done.
232 return false;
233
234 // Walk the module list.
235 MODULEENTRY32 module = {sizeof(module)};
236 if (!::Module32First(snap.Get(), &module))
237 return false;
238
239 do {
240 ModuleEvent event = {};
241 event.event_type = MODULE_ALREADY_LOADED;
242 event.module_path = base::FilePath(module.szExePath);
243 event.load_address = module.modBaseAddr;
244 event.size = module.modBaseSize;
245 Notify(event);
246 } while (::Module32Next(snap.Get(), &module));
247
248 return true;
249 }
250
251 void ModuleWatcher::Notify(const ModuleEvent& event) {
252 // Forward the notification to each registered observer.
253 observer_list_->Notify(FROM_HERE, &Observer::Notify, event);
254 }
255
256 ModuleWatcher::Observer::Observer(const OnModuleEventCallback& callback,
257 ModuleWatcher* module_watcher)
258 : callback_(callback), module_watcher_(module_watcher) {
259 module_watcher_->observer_list_->AddObserver(this);
260 }
261
262 ModuleWatcher::Observer::~Observer() {
263 module_watcher_->observer_list_->RemoveObserver(this);
264 }
265
266 void ModuleWatcher::Observer::Notify(const ModuleEvent& event) {
267 // Forward the observer to the callback. This all happens on the same thread
268 // on which the Observer was created.
269 callback_.Run(event);
270 }
271
272 } // namespace conflicts
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698