PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/sandbox/win/src/win_utils.cc

https://gitlab.com/0072016/Facebook-SDK-
C++ | 428 lines | 394 code | 23 blank | 11 comment | 18 complexity | 80655ff69322a1344410f1b6f6cd391c MD5 | raw file
  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.
  4. #include "sandbox/win/src/win_utils.h"
  5. #include <stddef.h>
  6. #include <map>
  7. #include <memory>
  8. #include "base/macros.h"
  9. #include "base/strings/string_util.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"
  14. namespace {
  15. // Holds the information about a known registry key.
  16. struct KnownReservedKey {
  17. const wchar_t* name;
  18. HKEY key;
  19. };
  20. // Contains all the known registry key by name and by handle.
  21. const KnownReservedKey kKnownKey[] = {
  22. { L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT },
  23. { L"HKEY_CURRENT_USER", HKEY_CURRENT_USER },
  24. { L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE},
  25. { L"HKEY_USERS", HKEY_USERS},
  26. { L"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA},
  27. { L"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT},
  28. { L"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT},
  29. { L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG},
  30. { L"HKEY_DYN_DATA", HKEY_DYN_DATA}
  31. };
  32. // These functions perform case independent path comparisons.
  33. bool EqualPath(const base::string16& first, const base::string16& second) {
  34. return _wcsicmp(first.c_str(), second.c_str()) == 0;
  35. }
  36. bool EqualPath(const base::string16& first, size_t first_offset,
  37. const base::string16& second, size_t second_offset) {
  38. return _wcsicmp(first.c_str() + first_offset,
  39. second.c_str() + second_offset) == 0;
  40. }
  41. bool EqualPath(const base::string16& first,
  42. const wchar_t* second, size_t second_len) {
  43. return _wcsnicmp(first.c_str(), second, second_len) == 0;
  44. }
  45. bool EqualPath(const base::string16& first, size_t first_offset,
  46. const wchar_t* second, size_t second_len) {
  47. return _wcsnicmp(first.c_str() + first_offset, second, second_len) == 0;
  48. }
  49. // Returns true if |path| starts with "\??\" and returns a path without that
  50. // component.
  51. bool IsNTPath(const base::string16& path, base::string16* trimmed_path ) {
  52. if ((path.size() < sandbox::kNTPrefixLen) ||
  53. (0 != path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix))) {
  54. *trimmed_path = path;
  55. return false;
  56. }
  57. *trimmed_path = path.substr(sandbox::kNTPrefixLen);
  58. return true;
  59. }
  60. // Returns true if |path| starts with "\Device\" and returns a path without that
  61. // component.
  62. bool IsDevicePath(const base::string16& path, base::string16* trimmed_path ) {
  63. if ((path.size() < sandbox::kNTDevicePrefixLen) ||
  64. (!EqualPath(path, sandbox::kNTDevicePrefix,
  65. sandbox::kNTDevicePrefixLen))) {
  66. *trimmed_path = path;
  67. return false;
  68. }
  69. *trimmed_path = path.substr(sandbox::kNTDevicePrefixLen);
  70. return true;
  71. }
  72. bool StartsWithDriveLetter(const base::string16& path) {
  73. if (path.size() < 3)
  74. return false;
  75. if (path[1] != L':' || path[2] != L'\\')
  76. return false;
  77. return (path[0] >= 'a' && path[0] <= 'z') ||
  78. (path[0] >= 'A' && path[0] <= 'Z');
  79. }
  80. const wchar_t kNTDotPrefix[] = L"\\\\.\\";
  81. const size_t kNTDotPrefixLen = arraysize(kNTDotPrefix) - 1;
  82. // Removes "\\\\.\\" from the path.
  83. void RemoveImpliedDevice(base::string16* path) {
  84. if (0 == path->compare(0, kNTDotPrefixLen, kNTDotPrefix))
  85. *path = path->substr(kNTDotPrefixLen);
  86. }
  87. } // namespace
  88. namespace sandbox {
  89. // Returns true if the provided path points to a pipe.
  90. bool IsPipe(const base::string16& path) {
  91. size_t start = 0;
  92. if (0 == path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix))
  93. start = sandbox::kNTPrefixLen;
  94. const wchar_t kPipe[] = L"pipe\\";
  95. if (path.size() < start + arraysize(kPipe) - 1)
  96. return false;
  97. return EqualPath(path, start, kPipe, arraysize(kPipe) - 1);
  98. }
  99. HKEY GetReservedKeyFromName(const base::string16& name) {
  100. for (size_t i = 0; i < arraysize(kKnownKey); ++i) {
  101. if (name == kKnownKey[i].name)
  102. return kKnownKey[i].key;
  103. }
  104. return NULL;
  105. }
  106. bool ResolveRegistryName(base::string16 name, base::string16* resolved_name) {
  107. for (size_t i = 0; i < arraysize(kKnownKey); ++i) {
  108. if (name.find(kKnownKey[i].name) == 0) {
  109. HKEY key;
  110. DWORD disposition;
  111. if (ERROR_SUCCESS != ::RegCreateKeyEx(kKnownKey[i].key, L"", 0, NULL, 0,
  112. MAXIMUM_ALLOWED, NULL, &key,
  113. &disposition))
  114. return false;
  115. bool result = GetPathFromHandle(key, resolved_name);
  116. ::RegCloseKey(key);
  117. if (!result)
  118. return false;
  119. *resolved_name += name.substr(wcslen(kKnownKey[i].name));
  120. return true;
  121. }
  122. }
  123. return false;
  124. }
  125. // |full_path| can have any of the following forms:
  126. // \??\c:\some\foo\bar
  127. // \Device\HarddiskVolume0\some\foo\bar
  128. // \??\HarddiskVolume0\some\foo\bar
  129. DWORD IsReparsePoint(const base::string16& full_path) {
  130. // Check if it's a pipe. We can't query the attributes of a pipe.
  131. if (IsPipe(full_path))
  132. return ERROR_NOT_A_REPARSE_POINT;
  133. base::string16 path;
  134. bool nt_path = IsNTPath(full_path, &path);
  135. bool has_drive = StartsWithDriveLetter(path);
  136. bool is_device_path = IsDevicePath(path, &path);
  137. if (!has_drive && !is_device_path && !nt_path)
  138. return ERROR_INVALID_NAME;
  139. bool added_implied_device = false;
  140. if (!has_drive) {
  141. path = base::string16(kNTDotPrefix) + path;
  142. added_implied_device = true;
  143. }
  144. base::string16::size_type last_pos = base::string16::npos;
  145. bool passed_once = false;
  146. do {
  147. path = path.substr(0, last_pos);
  148. DWORD attributes = ::GetFileAttributes(path.c_str());
  149. if (INVALID_FILE_ATTRIBUTES == attributes) {
  150. DWORD error = ::GetLastError();
  151. if (error != ERROR_FILE_NOT_FOUND &&
  152. error != ERROR_PATH_NOT_FOUND &&
  153. error != ERROR_INVALID_NAME) {
  154. // Unexpected error.
  155. if (passed_once && added_implied_device &&
  156. (path.rfind(L'\\') == kNTDotPrefixLen - 1)) {
  157. break;
  158. }
  159. NOTREACHED_NT();
  160. return error;
  161. }
  162. } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) {
  163. // This is a reparse point.
  164. return ERROR_SUCCESS;
  165. }
  166. passed_once = true;
  167. last_pos = path.rfind(L'\\');
  168. } while (last_pos > 2); // Skip root dir.
  169. return ERROR_NOT_A_REPARSE_POINT;
  170. }
  171. // We get a |full_path| of the forms accepted by IsReparsePoint(), and the name
  172. // we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
  173. bool SameObject(HANDLE handle, const wchar_t* full_path) {
  174. // Check if it's a pipe.
  175. if (IsPipe(full_path))
  176. return true;
  177. base::string16 actual_path;
  178. if (!GetPathFromHandle(handle, &actual_path))
  179. return false;
  180. base::string16 path(full_path);
  181. DCHECK_NT(!path.empty());
  182. // This may end with a backslash.
  183. const wchar_t kBackslash = '\\';
  184. if (path.back() == kBackslash)
  185. path = path.substr(0, path.length() - 1);
  186. // Perfect match (case-insesitive check).
  187. if (EqualPath(actual_path, path))
  188. return true;
  189. bool nt_path = IsNTPath(path, &path);
  190. bool has_drive = StartsWithDriveLetter(path);
  191. if (!has_drive && nt_path) {
  192. base::string16 simple_actual_path;
  193. if (!IsDevicePath(actual_path, &simple_actual_path))
  194. return false;
  195. // Perfect match (case-insesitive check).
  196. return (EqualPath(simple_actual_path, path));
  197. }
  198. if (!has_drive)
  199. return false;
  200. // We only need 3 chars, but let's alloc a buffer for four.
  201. wchar_t drive[4] = {0};
  202. wchar_t vol_name[MAX_PATH];
  203. memcpy(drive, &path[0], 2 * sizeof(*drive));
  204. // We'll get a double null terminated string.
  205. DWORD vol_length = ::QueryDosDeviceW(drive, vol_name, MAX_PATH);
  206. if (vol_length < 2 || vol_length == MAX_PATH)
  207. return false;
  208. // Ignore the nulls at the end.
  209. vol_length = static_cast<DWORD>(wcslen(vol_name));
  210. // The two paths should be the same length.
  211. if (vol_length + path.size() - 2 != actual_path.size())
  212. return false;
  213. // Check up to the drive letter.
  214. if (!EqualPath(actual_path, vol_name, vol_length))
  215. return false;
  216. // Check the path after the drive letter.
  217. if (!EqualPath(actual_path, vol_length, path, 2))
  218. return false;
  219. return true;
  220. }
  221. // Paths like \Device\HarddiskVolume0\some\foo\bar are assumed to be already
  222. // expanded.
  223. bool ConvertToLongPath(base::string16* path) {
  224. if (IsPipe(*path))
  225. return true;
  226. base::string16 temp_path;
  227. if (IsDevicePath(*path, &temp_path))
  228. return false;
  229. bool is_nt_path = IsNTPath(temp_path, &temp_path);
  230. bool added_implied_device = false;
  231. if (!StartsWithDriveLetter(temp_path) && is_nt_path) {
  232. temp_path = base::string16(kNTDotPrefix) + temp_path;
  233. added_implied_device = true;
  234. }
  235. DWORD size = MAX_PATH;
  236. std::unique_ptr<wchar_t[]> long_path_buf(new wchar_t[size]);
  237. DWORD return_value = ::GetLongPathName(temp_path.c_str(), long_path_buf.get(),
  238. size);
  239. while (return_value >= size) {
  240. size *= 2;
  241. long_path_buf.reset(new wchar_t[size]);
  242. return_value = ::GetLongPathName(temp_path.c_str(), long_path_buf.get(),
  243. size);
  244. }
  245. DWORD last_error = ::GetLastError();
  246. if (0 == return_value && (ERROR_FILE_NOT_FOUND == last_error ||
  247. ERROR_PATH_NOT_FOUND == last_error ||
  248. ERROR_INVALID_NAME == last_error)) {
  249. // The file does not exist, but maybe a sub path needs to be expanded.
  250. base::string16::size_type last_slash = temp_path.rfind(L'\\');
  251. if (base::string16::npos == last_slash)
  252. return false;
  253. base::string16 begin = temp_path.substr(0, last_slash);
  254. base::string16 end = temp_path.substr(last_slash);
  255. if (!ConvertToLongPath(&begin))
  256. return false;
  257. // Ok, it worked. Let's reset the return value.
  258. temp_path = begin + end;
  259. return_value = 1;
  260. } else if (0 != return_value) {
  261. temp_path = long_path_buf.get();
  262. }
  263. if (return_value != 0) {
  264. if (added_implied_device)
  265. RemoveImpliedDevice(&temp_path);
  266. if (is_nt_path) {
  267. *path = kNTPrefix;
  268. *path += temp_path;
  269. } else {
  270. *path = temp_path;
  271. }
  272. return true;
  273. }
  274. return false;
  275. }
  276. bool GetPathFromHandle(HANDLE handle, base::string16* path) {
  277. NtQueryObjectFunction NtQueryObject = NULL;
  278. ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
  279. OBJECT_NAME_INFORMATION initial_buffer;
  280. OBJECT_NAME_INFORMATION* name = &initial_buffer;
  281. ULONG size = sizeof(initial_buffer);
  282. // Query the name information a first time to get the size of the name.
  283. // Windows XP requires that the size of the buffer passed in here be != 0.
  284. NTSTATUS status = NtQueryObject(handle, ObjectNameInformation, name, size,
  285. &size);
  286. std::unique_ptr<BYTE[]> name_ptr;
  287. if (size) {
  288. name_ptr.reset(new BYTE[size]);
  289. name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(name_ptr.get());
  290. // Query the name information a second time to get the name of the
  291. // object referenced by the handle.
  292. status = NtQueryObject(handle, ObjectNameInformation, name, size, &size);
  293. }
  294. if (STATUS_SUCCESS != status)
  295. return false;
  296. path->assign(name->ObjectName.Buffer, name->ObjectName.Length /
  297. sizeof(name->ObjectName.Buffer[0]));
  298. return true;
  299. }
  300. bool GetNtPathFromWin32Path(const base::string16& path,
  301. base::string16* nt_path) {
  302. HANDLE file = ::CreateFileW(path.c_str(), 0,
  303. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
  304. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
  305. if (file == INVALID_HANDLE_VALUE)
  306. return false;
  307. bool rv = GetPathFromHandle(file, nt_path);
  308. ::CloseHandle(file);
  309. return rv;
  310. }
  311. bool WriteProtectedChildMemory(HANDLE child_process, void* address,
  312. const void* buffer, size_t length) {
  313. // First, remove the protections.
  314. DWORD old_protection;
  315. if (!::VirtualProtectEx(child_process, address, length,
  316. PAGE_WRITECOPY, &old_protection))
  317. return false;
  318. SIZE_T written;
  319. bool ok = ::WriteProcessMemory(child_process, address, buffer, length,
  320. &written) && (length == written);
  321. // Always attempt to restore the original protection.
  322. if (!::VirtualProtectEx(child_process, address, length,
  323. old_protection, &old_protection))
  324. return false;
  325. return ok;
  326. }
  327. }; // namespace sandbox
  328. void ResolveNTFunctionPtr(const char* name, void* ptr) {
  329. static volatile HMODULE ntdll = NULL;
  330. if (!ntdll) {
  331. HMODULE ntdll_local = ::GetModuleHandle(sandbox::kNtdllName);
  332. // Use PEImage to sanity-check that we have a valid ntdll handle.
  333. base::win::PEImage ntdll_peimage(ntdll_local);
  334. CHECK_NT(ntdll_peimage.VerifyMagic());
  335. // Race-safe way to set static ntdll.
  336. ::InterlockedCompareExchangePointer(
  337. reinterpret_cast<PVOID volatile*>(&ntdll), ntdll_local, NULL);
  338. }
  339. CHECK_NT(ntdll);
  340. FARPROC* function_ptr = reinterpret_cast<FARPROC*>(ptr);
  341. *function_ptr = ::GetProcAddress(ntdll, name);
  342. CHECK_NT(*function_ptr);
  343. }