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

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

Issue 2473783005: [Win] Create ModuleWatcher. (Closed)
Patch Set: Small cleanup. 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 <windows.h>
8 #include <tlhelp32.h>
9 #include <winternl.h> // For UNICODE_STRING.
10
11 #include "base/lazy_instance.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/win/scoped_handle.h"
15
16 // These structures and functions are documented in MSDN, see
17 // http://msdn.microsoft.com/en-us/library/gg547638(v=vs.85).aspx
18 // there are however no headers or import libraries available in the
19 // Platform SDK. They are declared outside of the anonymous namespace to
20 // allow them to be forward declared in the header file.
21 enum {
22 // The DLL was loaded. The NotificationData parameter points to a
23 // LDR_DLL_LOADED_NOTIFICATION_DATA structure.
24 LDR_DLL_NOTIFICATION_REASON_LOADED = 1,
25 // The DLL was unloaded. The NotificationData parameter points to a
26 // LDR_DLL_UNLOADED_NOTIFICATION_DATA structure.
27 LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2,
28 };
29
30 // Structure that is used for module load notifications.
31 struct LDR_DLL_LOADED_NOTIFICATION_DATA {
32 // Reserved.
33 ULONG Flags;
34 // The full path name of the DLL module.
35 PCUNICODE_STRING FullDllName;
36 // The base file name of the DLL module.
37 PCUNICODE_STRING BaseDllName;
38 // A pointer to the base address for the DLL in memory.
39 PVOID DllBase;
40 // The size of the DLL image, in bytes.
41 ULONG SizeOfImage;
42 };
43 using PLDR_DLL_LOADED_NOTIFICATION_DATA = LDR_DLL_LOADED_NOTIFICATION_DATA*;
44
45 // Structure that is used for module unload notifications.
46 struct LDR_DLL_UNLOADED_NOTIFICATION_DATA {
47 // Reserved.
48 ULONG Flags;
49 // The full path name of the DLL module.
50 PCUNICODE_STRING FullDllName;
51 // The base file name of the DLL module.
52 PCUNICODE_STRING BaseDllName;
53 // A pointer to the base address for the DLL in memory.
54 PVOID DllBase;
55 // The size of the DLL image, in bytes.
56 ULONG SizeOfImage;
57 };
58 using PLDR_DLL_UNLOADED_NOTIFICATION_DATA = LDR_DLL_UNLOADED_NOTIFICATION_DATA*;
59
60 // Union that is used for notifications.
61 union LDR_DLL_NOTIFICATION_DATA {
62 LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
63 LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
64 };
65 using PLDR_DLL_NOTIFICATION_DATA = LDR_DLL_NOTIFICATION_DATA*;
66
67 // Signature of the notification callback function.
68 using PLDR_DLL_NOTIFICATION_FUNCTION =
69 VOID(CALLBACK*)(ULONG notification_reason,
70 const LDR_DLL_NOTIFICATION_DATA* notification_data,
71 PVOID context);
72
73 // Signatures of the functions used for registering DLL notification callbacks.
74 using LdrRegisterDllNotificationFunc =
75 NTSTATUS(NTAPI*)(ULONG flags,
76 PLDR_DLL_NOTIFICATION_FUNCTION notification_function,
77 PVOID context,
78 PVOID* cookie);
79 using LdrUnregisterDllNotificationFunc = NTSTATUS(NTAPI*)(PVOID cookie);
80
81 namespace {
82
83 // Global lock for ensuring synchronization of destruction and notifications.
84 base::LazyInstance<base::Lock>::Leaky g_module_watcher_lock =
grt (UTC plus 2) 2016/11/14 20:30:31 #include "base/synchronization/lock.h"
chrisha 2016/11/14 21:38:39 Done.
85 LAZY_INSTANCE_INITIALIZER;
86 // Global pointer to the singleton ModuleWatcher, if one exists. Under
87 // |module_watcher_lock|.
88 ModuleWatcher* g_module_watcher_instance = nullptr;
89
90 // Names of the DLL notification registration functions. These are exported by
91 // ntdll.
92 constexpr wchar_t kNtDll[] = L"ntdll.dll";
93 constexpr char kLdrRegisterDllNotification[] = "LdrRegisterDllNotification";
94 constexpr char kLdrUnregisterDllNotification[] = "LdrUnregisterDllNotification";
95
96 // Helper function for converting a UNICODE_STRING to a UTF8 std::string.
97 std::string ToString(const UNICODE_STRING* str) {
grt (UTC plus 2) 2016/11/14 20:30:31 #include <string>
chrisha 2016/11/14 21:38:39 Done.
98 std::string s;
99 base::WideToUTF8(str->Buffer, str->Length / sizeof(wchar_t), &s);
100 return s;
101 }
102
103 template <typename NotificationDataType>
104 void OnModuleEvent(mojom::ModuleEventType event_type,
105 const NotificationDataType& notification_data,
106 const ModuleWatcher::OnModuleEventCallback& callback) {
107 mojom::ModuleEvent event;
108 event.event_type = event_type;
109 event.module_path = ToString(notification_data.FullDllName);
110 event.load_address = reinterpret_cast<uintptr_t>(notification_data.DllBase);
111 event.size = notification_data.SizeOfImage;
112 callback.Run(event);
113 }
114
115 } // namespace
116
117 // static
118 std::unique_ptr<ModuleWatcher> ModuleWatcher::Create(
119 const OnModuleEventCallback& callback) {
120 // If a ModuleWatcher already exists then bail out.
121 base::AutoLock lock(g_module_watcher_lock.Get());
122 if (g_module_watcher_instance)
123 return nullptr;
124
125 // This thread acquired the right to create a ModuleWatcher, so do so.
126 g_module_watcher_instance = new ModuleWatcher(callback);
127 return base::WrapUnique(g_module_watcher_instance);
128 }
129
130 ModuleWatcher::~ModuleWatcher() {
131 // As soon as |g_module_watcher_instance| is null any dispatched callbacks
132 // will be silently absorbed by LoaderNotificationCallback.
133 base::AutoLock lock(g_module_watcher_lock.Get());
134 DCHECK_EQ(g_module_watcher_instance, this);
135 g_module_watcher_instance = nullptr;
136 UnregisterDllNotificationCallback();
137 }
138
139 void ModuleWatcher::RegisterDllNotificationCallback() {
140 LdrRegisterDllNotificationFunc reg_fn =
141 reinterpret_cast<LdrRegisterDllNotificationFunc>(::GetProcAddress(
142 ::GetModuleHandle(kNtDll), kLdrRegisterDllNotification));
143 if (!reg_fn)
grt (UTC plus 2) 2016/11/14 20:30:31 nit: without the return value, i think this reads
chrisha 2016/11/14 21:38:39 Done.
144 return;
145
146 reg_fn(0, &LoaderNotificationCallback, this, &dll_notification_cookie_);
147 }
148
149 void ModuleWatcher::UnregisterDllNotificationCallback() {
150 LdrUnregisterDllNotificationFunc unreg_fn =
151 reinterpret_cast<LdrUnregisterDllNotificationFunc>(::GetProcAddress(
152 ::GetModuleHandle(kNtDll), kLdrUnregisterDllNotification));
153 if (!unreg_fn)
154 return;
155
156 unreg_fn(dll_notification_cookie_);
157 }
158
159 void ModuleWatcher::EnumerateAlreadyLoadedModules() {
160 // Get all modules in the current process. According to MSDN,
161 // CreateToolhelp32Snapshot should be retried as long as its returning
162 // ERROR_BAD_LENGTH. To avoid locking up here a retry limit is enforced.
163 base::win::ScopedHandle snap;
164 DWORD process_id = ::GetCurrentProcessId();
165 for (size_t i = 0; i < 5; ++i) {
166 snap.Set(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
167 process_id));
168 if (snap.IsValid())
169 break;
170 if (::GetLastError() != ERROR_BAD_LENGTH)
171 return;
172 }
173 if (!snap.IsValid())
174 return;
175
176 // Walk the module list.
177 MODULEENTRY32 module = {sizeof(module)};
178 for (BOOL result = ::Module32First(snap.Get(), &module); result != FALSE;
179 result = ::Module32Next(snap.Get(), &module)) {
180 std::string s;
grt (UTC plus 2) 2016/11/14 20:30:31 move this outside of the loop to save on heap chur
chrisha 2016/11/14 21:38:39 Done.
181 base::WideToUTF8(module.szExePath, ::wcslen(module.szExePath), &s);
182
183 mojom::ModuleEvent event;
184 event.event_type = mojom::ModuleEventType::MODULE_ALREADY_LOADED;
185 event.module_path = s;
186 event.load_address = reinterpret_cast<uintptr_t>(module.modBaseAddr);
187 event.size = module.modBaseSize;
188
189 callback_.Run(event);
190 }
191
192 return;
193 }
194
195 // static
196 ModuleWatcher::OnModuleEventCallback ModuleWatcher::GetCallbackForContext(
197 void* context) {
198 base::AutoLock lock(g_module_watcher_lock.Get());
199 if (context != g_module_watcher_instance)
200 return ModuleWatcher::OnModuleEventCallback();
grt (UTC plus 2) 2016/11/14 20:30:31 omit "ModuleWatcher::"
chrisha 2016/11/14 21:38:39 Done.
201 return g_module_watcher_instance->callback_;
202 }
203
204 // static
205 void __stdcall ModuleWatcher::LoaderNotificationCallback(
206 unsigned long notification_reason,
207 const LDR_DLL_NOTIFICATION_DATA* notification_data,
208 void* context) {
209 auto callback = GetCallbackForContext(context);
210 if (callback.is_null())
211 return;
212
213 switch (notification_reason) {
214 case LDR_DLL_NOTIFICATION_REASON_LOADED:
215 OnModuleEvent(mojom::ModuleEventType::MODULE_LOADED,
216 notification_data->Loaded, callback);
217 break;
218
219 case LDR_DLL_NOTIFICATION_REASON_UNLOADED:
220 OnModuleEvent(mojom::ModuleEventType::MODULE_UNLOADED,
221 notification_data->Unloaded, callback);
222 break;
223
224 default:
225 // This is unexpected, but not a reason to crash.
226 NOTREACHED() << "Unknown LDR_DLL_NOTIFICATION_REASON: "
227 << notification_reason;
228 }
229 }
230
231 ModuleWatcher::ModuleWatcher(const OnModuleEventCallback& callback)
232 : callback_(callback) {
233 RegisterDllNotificationCallback();
234 EnumerateAlreadyLoadedModules();
235 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698