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

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

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

Powered by Google App Engine
This is Rietveld 408576698