File: enumerate_shell_extensions.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (247 lines) | stat: -rw-r--r-- 8,724 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/win/conflicts/enumerate_shell_extensions.h"

#include <string>
#include <utility>

#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/ref_counted.h"
#include "base/strings/strcat_win.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/win/registry.h"
#include "chrome/browser/win/conflicts/module_info_util.h"

// The following link explains how Shell extensions are registered on Windows.
// Different types of handlers can be applied to different types of Shell
// objects.
// https://docs.microsoft.com/en-us/windows/desktop/shell/reg-shell-exts

namespace {

// The different kinds of shell objects that can be affected by a shell
// extension.
constexpr wchar_t kEverything[] = L"*";
constexpr wchar_t kAllFileSystemObjects[] = L"AllFileSystemObjects";
constexpr wchar_t kDesktopBackground[] = L"DesktopBackground";
constexpr wchar_t kDirectory[] = L"Directory";
constexpr wchar_t kDirectoryBackground[] = L"Directory\\Background";
constexpr wchar_t kDrive[] = L"Drive";
constexpr wchar_t kFolder[] = L"Folder";
constexpr wchar_t kNetServer[] = L"NetServer";
constexpr wchar_t kNetShare[] = L"NetShare";
constexpr wchar_t kNetwork[] = L"Network";
constexpr wchar_t kPrinters[] = L"Printers";

// Retrieves the path to the registry key that contains all the shell extensions
// of type |shell_extension_type| that apply to |shell_object_type|.
std::wstring GetShellExtensionTypePath(const wchar_t* shell_extension_type,
                                       const wchar_t* shell_object_type) {
  return base::StrCat(
      {shell_object_type, L"\\shellex\\", shell_extension_type});
}

// Returns the path to the DLL for an InProcServer32 registration.
base::FilePath GetInProcServerPath(const wchar_t* guid) {
  const std::wstring key = GuidToClsid(guid);

  base::win::RegKey clsid;
  if (clsid.Open(HKEY_CLASSES_ROOT, key.c_str(), KEY_QUERY_VALUE) !=
      ERROR_SUCCESS) {
    return base::FilePath();
  }

  std::wstring dll_path;
  if (clsid.ReadValue(L"", &dll_path) != ERROR_SUCCESS)
    return base::FilePath();

  return base::FilePath(std::move(dll_path));
}

// Reads all the shell extensions of type |shell_extension_type| that are
// applied to |shell_object_types| and forwards them to |callback|.
void ReadShellExtensions(
    const wchar_t* shell_extension_type,
    const wchar_t* shell_object_type,
    const base::RepeatingCallback<void(const base::FilePath&)>& callback) {
  std::wstring path =
      GetShellExtensionTypePath(shell_extension_type, shell_object_type);

  DCHECK_NE(path.back(), L'\\');

  std::wstring guid;
  for (base::win::RegistryKeyIterator iter(HKEY_CLASSES_ROOT, path.c_str());
       iter.Valid(); ++iter) {
    std::wstring shell_extension_reg_path = path + L"\\" + iter.Name();
    base::win::RegKey reg_key(
        HKEY_CLASSES_ROOT, shell_extension_reg_path.c_str(), KEY_QUERY_VALUE);
    if (!reg_key.Valid())
      continue;

    guid.clear();
    reg_key.ReadValue(nullptr, &guid);
    if (guid.empty())
      continue;

    base::FilePath shell_extension_path = GetInProcServerPath(guid.c_str());
    if (shell_extension_path.empty())
      continue;

    callback.Run(shell_extension_path);
  }
}

// Reads all the shell extensions that are in the Approved list and forwards
// them to |callback|.
void ReadApprovedShellExtensions(
    HKEY parent,
    const base::RepeatingCallback<void(const base::FilePath&)>& callback) {
  for (base::win::RegistryValueIterator iter(
           parent, kApprovedShellExtensionRegistryKey);
       iter.Valid(); ++iter) {
    // Skip the key's default value.
    if (!*iter.Name())
      continue;

    base::FilePath shell_extension_path = GetInProcServerPath(iter.Name());
    if (shell_extension_path.empty())
      continue;

    callback.Run(shell_extension_path);
  }
}

// Reads all the shell extensions of type ColumnHandlers.
void ReadColumnHandlers(
    const base::RepeatingCallback<void(const base::FilePath&)>& callback) {
  // Column handlers can only be applied to folders.
  ReadShellExtensions(L"ColumnHandlers", kFolder, callback);
}

// Reads all the shell extensions of type CopyHookHandlers.
void ReadCopyHookHandlers(
    const base::RepeatingCallback<void(const base::FilePath&)>& callback) {
  static constexpr const wchar_t* kSupportedShellObjects[] = {
      kDirectory,
      kPrinters,
  };

  for (const auto* shell_object : kSupportedShellObjects)
    ReadShellExtensions(L"CopyHookHandlers", shell_object, callback);
}

// Reads all the shell extensions of type DragDropHandlers.
void ReadDragDropHandlers(
    const base::RepeatingCallback<void(const base::FilePath&)>& callback) {
  static constexpr const wchar_t* kSupportedShellObjects[] = {
      kDirectory,
      kDrive,
      kFolder,
  };

  for (const auto* shell_object : kSupportedShellObjects)
    ReadShellExtensions(L"DragDropHandlers", shell_object, callback);
}

// Reads all the shell extensions of type ContextMenuHandlers and
// PropertySheetHandlers.
void ReadContextMenuAndPropertySheetHandlers(
    const base::RepeatingCallback<void(const base::FilePath&)>& callback) {
  static constexpr const wchar_t* kHandlerTypes[] = {
      L"ContextMenuHandlers",
      L"PropertySheetHandlers",
  };

  // This list is not exhaustive and does not cover cases where a shell
  // extension is installed for a specific file extension or a specific
  // executable. It should still pick up all the general-purpose shell
  // extensions.
  static constexpr const wchar_t* kSupportedShellObjects[] = {
      kEverything,
      kAllFileSystemObjects,
      kFolder,
      kDirectory,
      kDirectoryBackground,
      kDesktopBackground,
      kDrive,
      kNetwork,
      kNetShare,
      kNetServer,
      kPrinters,
  };

  for (const auto* handler_type : kHandlerTypes) {
    for (const auto* shell_object : kSupportedShellObjects)
      ReadShellExtensions(handler_type, shell_object, callback);
  }
}

// Retrieves the module size and time date stamp for the shell extension and
// forwards it to the callback on |task_runner|.
void OnShellExtensionPathEnumerated(
    scoped_refptr<base::SequencedTaskRunner> task_runner,
    OnShellExtensionEnumeratedCallback on_shell_extension_enumerated,
    const base::FilePath& path) {
  uint32_t size_of_image = 0;
  uint32_t time_date_stamp = 0;
  if (!GetModuleImageSizeAndTimeDateStamp(path, &size_of_image,
                                          &time_date_stamp)) {
    return;
  }

  task_runner->PostTask(
      FROM_HERE, base::BindOnce(std::move(on_shell_extension_enumerated), path,
                                size_of_image, time_date_stamp));
}

void EnumerateShellExtensionsOnBlockingSequence(
    scoped_refptr<base::SequencedTaskRunner> task_runner,
    OnShellExtensionEnumeratedCallback on_shell_extension_enumerated,
    base::OnceClosure on_enumeration_finished) {
  internal::EnumerateShellExtensionPaths(
      base::BindRepeating(&OnShellExtensionPathEnumerated, task_runner,
                          std::move(on_shell_extension_enumerated)));

  task_runner->PostTask(FROM_HERE, std::move(on_enumeration_finished));
}

}  // namespace

const wchar_t kApprovedShellExtensionRegistryKey[] =
    L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved";

void EnumerateShellExtensions(
    OnShellExtensionEnumeratedCallback on_shell_extension_enumerated,
    base::OnceClosure on_enumeration_finished) {
  base::ThreadPool::PostTask(
      FROM_HERE,
      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
      base::BindOnce(&EnumerateShellExtensionsOnBlockingSequence,
                     base::SequencedTaskRunner::GetCurrentDefault(),
                     std::move(on_shell_extension_enumerated),
                     std::move(on_enumeration_finished)));
}

namespace internal {

void EnumerateShellExtensionPaths(
    const base::RepeatingCallback<void(const base::FilePath&)>& callback) {
  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                base::BlockingType::MAY_BLOCK);

  ReadApprovedShellExtensions(HKEY_LOCAL_MACHINE, callback);
  ReadApprovedShellExtensions(HKEY_CURRENT_USER, callback);
  ReadColumnHandlers(callback);
  ReadCopyHookHandlers(callback);
  ReadDragDropHandlers(callback);
  ReadContextMenuAndPropertySheetHandlers(callback);
}

}  // namespace internal