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

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

Issue 2473783005: [Win] Create ModuleWatcher. (Closed)
Patch Set: Refactor threading and observer model. 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>
grt (UTC plus 2) 2016/11/08 14:07:52 nit: windows.h often needs to be included before o
chrisha 2016/11/08 21:45:21 Done.
9 #include <winternl.h> // For UNICODE_STRING.
10
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/win/scoped_handle.h"
13
14 namespace conflicts {
15
16 namespace {
17
18 // These structures and functions are documented in MSDN, see
19 // http://msdn.microsoft.com/en-us/library/gg547638(v=vs.85).aspx
20 // there are however no headers or import libraries available in the
21 // Platform SDK.
22 enum {
23 // The DLL was loaded. The NotificationData parameter points to a
24 // LDR_DLL_LOADED_NOTIFICATION_DATA structure.
25 LDR_DLL_NOTIFICATION_REASON_LOADED = 1,
26 // The DLL was unloaded. The NotificationData parameter points to a
27 // LDR_DLL_UNLOADED_NOTIFICATION_DATA structure.
28 LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2,
29 };
30
31 // Structure that is used for module load notifications.
32 typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA {
33 // Reserved.
34 ULONG Flags;
35 // The full path name of the DLL module.
36 PCUNICODE_STRING FullDllName;
37 // The base file name of the DLL module.
38 PCUNICODE_STRING BaseDllName;
39 // A pointer to the base address for the DLL in memory.
40 PVOID DllBase;
41 // The size of the DLL image, in bytes.
42 ULONG SizeOfImage;
43 } LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;
44
45 // Structure that is used for module unload notifications.
46 typedef 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 } LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
58
59 // Union that is used for notifications.
60 typedef union _LDR_DLL_NOTIFICATION_DATA {
61 LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
62 LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
63 } LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;
64
65 // Signature of the otification callback function.
66 typedef VOID(CALLBACK* PLDR_DLL_NOTIFICATION_FUNCTION)(
grt (UTC plus 2) 2016/11/08 14:07:52 nit: using rather than typedef?
chrisha 2016/11/08 21:45:21 Done.
67 ULONG notification_reason,
68 const LDR_DLL_NOTIFICATION_DATA* notification_data,
69 PVOID context);
70
71 // Signatures of the functions used for registering DLL notification callbacks.
72 typedef NTSTATUS(NTAPI* LdrRegisterDllNotificationFunc)(
73 ULONG flags,
74 PLDR_DLL_NOTIFICATION_FUNCTION notification_function,
75 PVOID context,
76 PVOID* cookie);
77 typedef NTSTATUS(NTAPI* LdrUnregisterDllNotificationFunc)(PVOID cookie);
78
79 // Names of the DLL notification registration functions. These are exported by
80 // ntdll.
81 constexpr wchar_t kNtDll[] = L"ntdll.dll";
82 constexpr char kLdrRegisterDllNotification[] = "LdrRegisterDllNotification";
83 constexpr char kLdrUnregisterDllNotification[] = "LdrUnregisterDllNotification";
84
85 // Helper function for converting a UNICODE_STRING to an 8-bit string piece.
grt (UTC plus 2) 2016/11/08 14:07:52 "an 8-bit string piece" -> "a UTF-8 string"
chrisha 2016/11/08 21:45:21 Done.
86 std::string ToString(const UNICODE_STRING* str) {
87 std::string s;
88 CHECK(base::WideToUTF8(str->Buffer, str->Length / sizeof(wchar_t), &s));
grt (UTC plus 2) 2016/11/08 14:07:52 why check?
chrisha 2016/11/08 21:45:21 Good point. Removed.
89 return s;
90 }
91
92 } // namespace
93
94 // Static helper class for forwarding system notifications to the ModuleWatcher.
95 // The notifications here will be called on the thread that is doing the DLL
96 // load, which could be on a variety of threads in Chrome, including threads
97 // injected by third parties.
98 class ModuleWatcher::Bridge {
grt (UTC plus 2) 2016/11/08 14:07:52 The style guide more or less says not to make a cl
chrisha 2016/11/08 21:45:21 I'm generally in favour of this, but I feel this c
99 public:
100 // This is invoked by the OS. This forwards to OnModuleLoaded or
101 // OnModuleUnloaded depending on |notification_reason|.
102 static void CALLBACK
103 LoaderNotificationCallback(ULONG notification_reason,
104 const LDR_DLL_NOTIFICATION_DATA* notification_data,
105 PVOID context);
106
107 // Invoked by LoaderNotification for LDR_DLL_NOTIFICATION_REASON_LOADED
108 // events.
109 static void OnModuleLoad(const LDR_DLL_LOADED_NOTIFICATION_DATA& load_data,
110 ModuleWatcher* module_watcher);
111
112 // Invoked by LoaderNotification for LDR_DLL_NOTIFICATION_REASON_UNLOADED
113 // events.
114 static void OnModuleUnload(
115 const LDR_DLL_UNLOADED_NOTIFICATION_DATA& unload_data,
116 ModuleWatcher* module_watcher);
117 };
118
119 // static
120 void ModuleWatcher::Bridge::LoaderNotificationCallback(
121 ULONG notification_reason,
122 const LDR_DLL_NOTIFICATION_DATA* notification_data,
123 PVOID context) {
124 ModuleWatcher* module_watcher = reinterpret_cast<ModuleWatcher*>(context);
grt (UTC plus 2) 2016/11/08 14:07:52 looks like this is racy since any thread could be
chrisha 2016/11/08 21:45:21 I had assumed (terrible idea) that the Ldr* calls
grt (UTC plus 2) 2016/11/09 08:37:52 What I was thinking is that the context ptr passed
chrisha 2016/11/09 15:38:07 Thanks for the detailed explanation. The only iss
grt (UTC plus 2) 2016/11/10 08:52:12 I think that's subjective. One could make the case
125 switch (notification_reason) {
126 case LDR_DLL_NOTIFICATION_REASON_LOADED:
127 return OnModuleLoad(notification_data->Loaded, module_watcher);
grt (UTC plus 2) 2016/11/08 14:07:52 returning void is a bit odd. i don't think that's
chrisha 2016/11/08 21:45:21 Saved me a "break" :) Done.
128
129 case LDR_DLL_NOTIFICATION_REASON_UNLOADED:
130 return OnModuleUnload(notification_data->Unloaded, module_watcher);
131
132 default:
133 NOTREACHED() << "Unknown LDR_DLL_NOTIFICATION_REASON: "
134 << notification_reason;
135 }
136 }
137
138 // static
139 void ModuleWatcher::Bridge::OnModuleLoad(
140 const LDR_DLL_LOADED_NOTIFICATION_DATA& load_data,
141 ModuleWatcher* module_watcher) {
142 mojom::ModuleEvent event;
143 event.event_type = mojom::ModuleEventType::MODULE_LOADED;
144 event.module_path = ToString(load_data.FullDllName);
145 event.load_address = reinterpret_cast<uintptr_t>(load_data.DllBase);
146 event.size = load_data.SizeOfImage;
147 return module_watcher->Notify(event);
148 }
149
150 // static
151 void ModuleWatcher::Bridge::OnModuleUnload(
152 const LDR_DLL_UNLOADED_NOTIFICATION_DATA& unload_data,
153 ModuleWatcher* module_watcher) {
154 /*mojom::ModuleEvent event = {
155 mojom::ModuleEventType::MODULE_UNLOADED,
156 ToString(unload_data.FullDllName),
157 reinterpret_cast<uintptr_t>(unload_data.DllBase),
158 unload_data.SizeOfImage};*/
159 mojom::ModuleEvent event;
160 event.event_type = mojom::ModuleEventType::MODULE_UNLOADED;
161 event.module_path = ToString(unload_data.FullDllName);
162 event.load_address = reinterpret_cast<uintptr_t>(unload_data.DllBase);
163 event.size = unload_data.SizeOfImage;
164 return module_watcher->Notify(event);
165 }
166
167 ModuleWatcher::ModuleWatcher(const OnModuleEventCallback& callback)
168 : callback_(callback), dll_notification_cookie_(nullptr) {
169 CHECK(RegisterDllNotificationCallback());
grt (UTC plus 2) 2016/11/08 14:07:52 should a failure here really prevent Chrome from r
chrisha 2016/11/08 21:45:21 No, it shouldn't. Done.
170 EnumerateAlreadyLoadedModules();
171 }
172
173 ModuleWatcher::~ModuleWatcher() {
174 CHECK(UnregisterDllNotificationCallback());
grt (UTC plus 2) 2016/11/08 14:07:51 same comment here
chrisha 2016/11/08 21:45:21 Ditto.
175 }
176
177 bool ModuleWatcher::RegisterDllNotificationCallback() {
178 LdrRegisterDllNotificationFunc reg_fn =
179 reinterpret_cast<LdrRegisterDllNotificationFunc>(::GetProcAddress(
180 ::GetModuleHandle(kNtDll), kLdrRegisterDllNotification));
181
182 if (!reg_fn)
183 return false;
184
185 NTSTATUS status = reg_fn(0, &Bridge::LoaderNotificationCallback, this,
186 &dll_notification_cookie_);
187 return status == 0;
188 }
189
190 bool ModuleWatcher::UnregisterDllNotificationCallback() {
191 LdrUnregisterDllNotificationFunc unreg_fn =
192 reinterpret_cast<LdrUnregisterDllNotificationFunc>(::GetProcAddress(
193 ::GetModuleHandle(kNtDll), kLdrUnregisterDllNotification));
194
195 if (!unreg_fn)
196 return false;
197
198 NTSTATUS status = unreg_fn(dll_notification_cookie_);
199 return status == 0;
200 }
201
202 bool ModuleWatcher::EnumerateAlreadyLoadedModules() {
203 // Get all modules in the current process.
204 base::win::ScopedHandle snap(
205 ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ::GetCurrentProcessId()));
grt (UTC plus 2) 2016/11/08 14:07:52 ?TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32?
chrisha 2016/11/08 21:45:21 Indeed... cut and paste code from XP days.
206 if (!snap.IsValid())
grt (UTC plus 2) 2016/11/08 14:07:52 msdn says to retry if it fails with ERROR_BAD_LENG
chrisha 2016/11/08 21:45:21 Yeah, not a terrible idea. I'll put an upper bound
207 return false;
208
209 // Walk the module list.
210 MODULEENTRY32 module = {sizeof(module)};
211 if (!::Module32First(snap.Get(), &module))
212 return false;
213
214 do {
215 std::string s;
216 CHECK(base::WideToUTF8(module.szExePath, ::wcslen(module.szExePath), &s));
grt (UTC plus 2) 2016/11/08 14:07:52 why crash?
grt (UTC plus 2) 2016/11/08 14:07:52 how about doing this directly into event.module_pa
chrisha 2016/11/08 21:45:21 Done.
chrisha 2016/11/08 21:45:21 It's a mojo::String, and not a std::string :(
217
218 mojom::ModuleEvent event;
219 event.event_type = mojom::ModuleEventType::MODULE_ALREADY_LOADED;
220 event.module_path = s;
221 event.load_address = reinterpret_cast<uintptr_t>(module.modBaseAddr);
222 event.size = module.modBaseSize;
223 Notify(event);
224 } while (::Module32Next(snap.Get(), &module));
225
226 return true;
227 }
228
229 void ModuleWatcher::Notify(const mojom::ModuleEvent& event) {
230 callback_.Run(event);
231 }
232
233 } // namespace conflicts
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698