1 // Copyright (c) 2011 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.
5 #include "sandbox/win/src/win_utils.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/win/pe_image.h"
11 #include "sandbox/win/src/internal_types.h"
12 #include "sandbox/win/src/nt_internals.h"
13 #include "sandbox/win/src/sandbox_nt_util.h"
17 // Holds the information about a known registry key.
18 struct KnownReservedKey
{
23 // Contains all the known registry key by name and by handle.
24 const KnownReservedKey kKnownKey
[] = {
25 { L
"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT
},
26 { L
"HKEY_CURRENT_USER", HKEY_CURRENT_USER
},
27 { L
"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE
},
28 { L
"HKEY_USERS", HKEY_USERS
},
29 { L
"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA
},
30 { L
"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT
},
31 { L
"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT
},
32 { L
"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG
},
33 { L
"HKEY_DYN_DATA", HKEY_DYN_DATA
}
36 // Returns true if the provided path points to a pipe.
37 bool IsPipe(const base::string16
& path
) {
39 if (0 == path
.compare(0, sandbox::kNTPrefixLen
, sandbox::kNTPrefix
))
40 start
= sandbox::kNTPrefixLen
;
42 const wchar_t kPipe
[] = L
"pipe\\";
43 return (0 == path
.compare(start
, arraysize(kPipe
) - 1, kPipe
));
50 HKEY
GetReservedKeyFromName(const base::string16
& name
) {
51 for (size_t i
= 0; i
< arraysize(kKnownKey
); ++i
) {
52 if (name
== kKnownKey
[i
].name
)
53 return kKnownKey
[i
].key
;
59 bool ResolveRegistryName(base::string16 name
, base::string16
* resolved_name
) {
60 for (size_t i
= 0; i
< arraysize(kKnownKey
); ++i
) {
61 if (name
.find(kKnownKey
[i
].name
) == 0) {
64 if (ERROR_SUCCESS
!= ::RegCreateKeyEx(kKnownKey
[i
].key
, L
"", 0, NULL
, 0,
65 MAXIMUM_ALLOWED
, NULL
, &key
,
69 bool result
= GetPathFromHandle(key
, resolved_name
);
75 *resolved_name
+= name
.substr(wcslen(kKnownKey
[i
].name
));
83 DWORD
IsReparsePoint(const base::string16
& full_path
, bool* result
) {
84 base::string16 path
= full_path
;
86 // Remove the nt prefix.
87 if (0 == path
.compare(0, kNTPrefixLen
, kNTPrefix
))
88 path
= path
.substr(kNTPrefixLen
);
90 // Check if it's a pipe. We can't query the attributes of a pipe.
96 base::string16::size_type last_pos
= base::string16::npos
;
99 path
= path
.substr(0, last_pos
);
101 DWORD attributes
= ::GetFileAttributes(path
.c_str());
102 if (INVALID_FILE_ATTRIBUTES
== attributes
) {
103 DWORD error
= ::GetLastError();
104 if (error
!= ERROR_FILE_NOT_FOUND
&&
105 error
!= ERROR_PATH_NOT_FOUND
&&
106 error
!= ERROR_INVALID_NAME
) {
111 } else if (FILE_ATTRIBUTE_REPARSE_POINT
& attributes
) {
112 // This is a reparse point.
114 return ERROR_SUCCESS
;
117 last_pos
= path
.rfind(L
'\\');
118 } while (last_pos
> 2); // Skip root dir.
121 return ERROR_SUCCESS
;
124 // We get a |full_path| of the form \??\c:\some\foo\bar, and the name that
125 // we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
126 bool SameObject(HANDLE handle
, const wchar_t* full_path
) {
127 base::string16
path(full_path
);
128 DCHECK_NT(!path
.empty());
130 // Check if it's a pipe.
134 base::string16 actual_path
;
135 if (!GetPathFromHandle(handle
, &actual_path
))
138 // This may end with a backslash.
139 const wchar_t kBackslash
= '\\';
140 if (path
[path
.length() - 1] == kBackslash
)
141 path
= path
.substr(0, path
.length() - 1);
143 // Perfect match (case-insesitive check).
144 if (0 == _wcsicmp(actual_path
.c_str(), path
.c_str()))
147 // Look for the drive letter.
148 size_t colon_pos
= path
.find(L
':');
149 if (colon_pos
== 0 || colon_pos
== base::string16::npos
)
152 // Only one character for the drive.
153 if (colon_pos
> 1 && path
[colon_pos
- 2] != kBackslash
)
156 // We only need 3 chars, but let's alloc a buffer for four.
157 wchar_t drive
[4] = {0};
158 wchar_t vol_name
[MAX_PATH
];
159 memcpy(drive
, &path
[colon_pos
- 1], 2 * sizeof(*drive
));
161 // We'll get a double null terminated string.
162 DWORD vol_length
= ::QueryDosDeviceW(drive
, vol_name
, MAX_PATH
);
163 if (vol_length
< 2 || vol_length
== MAX_PATH
)
166 // Ignore the nulls at the end.
167 vol_length
= static_cast<DWORD
>(wcslen(vol_name
));
169 // The two paths should be the same length.
170 if (vol_length
+ path
.size() - (colon_pos
+ 1) != actual_path
.size())
173 // Check up to the drive letter.
174 if (0 != _wcsnicmp(actual_path
.c_str(), vol_name
, vol_length
))
177 // Check the path after the drive letter.
178 if (0 != _wcsicmp(&actual_path
[vol_length
], &path
[colon_pos
+ 1]))
184 bool ConvertToLongPath(const base::string16
& short_path
,
185 base::string16
* long_path
) {
186 // Check if the path is a NT path.
187 bool is_nt_path
= false;
188 base::string16 path
= short_path
;
189 if (0 == path
.compare(0, kNTPrefixLen
, kNTPrefix
)) {
190 path
= path
.substr(kNTPrefixLen
);
194 DWORD size
= MAX_PATH
;
195 scoped_ptr
<wchar_t[]> long_path_buf(new wchar_t[size
]);
197 DWORD return_value
= ::GetLongPathName(path
.c_str(), long_path_buf
.get(),
199 while (return_value
>= size
) {
201 long_path_buf
.reset(new wchar_t[size
]);
202 return_value
= ::GetLongPathName(path
.c_str(), long_path_buf
.get(), size
);
205 DWORD last_error
= ::GetLastError();
206 if (0 == return_value
&& (ERROR_FILE_NOT_FOUND
== last_error
||
207 ERROR_PATH_NOT_FOUND
== last_error
||
208 ERROR_INVALID_NAME
== last_error
)) {
209 // The file does not exist, but maybe a sub path needs to be expanded.
210 base::string16::size_type last_slash
= path
.rfind(L
'\\');
211 if (base::string16::npos
== last_slash
)
214 base::string16 begin
= path
.substr(0, last_slash
);
215 base::string16 end
= path
.substr(last_slash
);
216 if (!ConvertToLongPath(begin
, &begin
))
219 // Ok, it worked. Let's reset the return value.
222 } else if (0 != return_value
) {
223 path
= long_path_buf
.get();
226 if (return_value
!= 0) {
228 *long_path
= kNTPrefix
;
240 bool GetPathFromHandle(HANDLE handle
, base::string16
* path
) {
241 NtQueryObjectFunction NtQueryObject
= NULL
;
242 ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject
);
244 OBJECT_NAME_INFORMATION initial_buffer
;
245 OBJECT_NAME_INFORMATION
* name
= &initial_buffer
;
246 ULONG size
= sizeof(initial_buffer
);
247 // Query the name information a first time to get the size of the name.
248 NTSTATUS status
= NtQueryObject(handle
, ObjectNameInformation
, name
, size
,
251 scoped_ptr
<OBJECT_NAME_INFORMATION
> name_ptr
;
253 name
= reinterpret_cast<OBJECT_NAME_INFORMATION
*>(new BYTE
[size
]);
254 name_ptr
.reset(name
);
256 // Query the name information a second time to get the name of the
257 // object referenced by the handle.
258 status
= NtQueryObject(handle
, ObjectNameInformation
, name
, size
, &size
);
261 if (STATUS_SUCCESS
!= status
)
264 path
->assign(name
->ObjectName
.Buffer
, name
->ObjectName
.Length
/
265 sizeof(name
->ObjectName
.Buffer
[0]));
269 bool GetNtPathFromWin32Path(const base::string16
& path
,
270 base::string16
* nt_path
) {
271 HANDLE file
= ::CreateFileW(path
.c_str(), 0,
272 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
273 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
274 if (file
== INVALID_HANDLE_VALUE
)
276 bool rv
= GetPathFromHandle(file
, nt_path
);
281 bool WriteProtectedChildMemory(HANDLE child_process
, void* address
,
282 const void* buffer
, size_t length
) {
283 // First, remove the protections.
284 DWORD old_protection
;
285 if (!::VirtualProtectEx(child_process
, address
, length
,
286 PAGE_WRITECOPY
, &old_protection
))
290 bool ok
= ::WriteProcessMemory(child_process
, address
, buffer
, length
,
291 &written
) && (length
== written
);
293 // Always attempt to restore the original protection.
294 if (!::VirtualProtectEx(child_process
, address
, length
,
295 old_protection
, &old_protection
))
301 }; // namespace sandbox
303 void ResolveNTFunctionPtr(const char* name
, void* ptr
) {
304 static volatile HMODULE ntdll
= NULL
;
307 HMODULE ntdll_local
= ::GetModuleHandle(sandbox::kNtdllName
);
308 // Use PEImage to sanity-check that we have a valid ntdll handle.
309 base::win::PEImage
ntdll_peimage(ntdll_local
);
310 CHECK_NT(ntdll_peimage
.VerifyMagic());
311 // Race-safe way to set static ntdll.
312 ::InterlockedCompareExchangePointer(
313 reinterpret_cast<PVOID
volatile*>(&ntdll
), ntdll_local
, NULL
);
318 FARPROC
* function_ptr
= reinterpret_cast<FARPROC
*>(ptr
);
319 *function_ptr
= ::GetProcAddress(ntdll
, name
);
320 CHECK_NT(*function_ptr
);