Chromium Code Reviews| Index: sandbox/win/src/win_utils.cc |
| diff --git a/sandbox/win/src/win_utils.cc b/sandbox/win/src/win_utils.cc |
| index c31c25e3af5f4705b6009007485dfc406ff62221..f851259d4292c9524303bf7bd6f2b6fd152fb302 100644 |
| --- a/sandbox/win/src/win_utils.cc |
| +++ b/sandbox/win/src/win_utils.cc |
| @@ -4,12 +4,17 @@ |
| #include "sandbox/win/src/win_utils.h" |
| +#include <psapi.h> |
| #include <stddef.h> |
| +#include <stdint.h> |
| #include <map> |
| #include <memory> |
| +#include <vector> |
| #include "base/macros.h" |
| +#include "base/numerics/safe_math.h" |
| +#include "base/strings/string16.h" |
| #include "base/strings/string_util.h" |
| #include "base/win/pe_image.h" |
| #include "sandbox/win/src/internal_types.h" |
| @@ -104,6 +109,45 @@ void RemoveImpliedDevice(base::string16* path) { |
| *path = path->substr(kNTDotPrefixLen); |
| } |
| +// Get the native path to the process. |
| +bool GetProcessPath(HANDLE process, base::string16* path) { |
| + wchar_t process_name[MAX_PATH]; |
| + DWORD size = MAX_PATH; |
| + if (::QueryFullProcessImageNameW(process, PROCESS_NAME_NATIVE, process_name, |
| + &size)) { |
| + *path = process_name; |
| + return true; |
| + } |
| + // Process name is potentially greater than MAX_PATH, try larger max size. |
| + std::vector<wchar_t> process_name_buffer(SHRT_MAX); |
| + size = SHRT_MAX; |
| + if (::QueryFullProcessImageNameW(process, PROCESS_NAME_NATIVE, |
| + &process_name_buffer[0], &size)) { |
| + *path = &process_name_buffer[0]; |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +// Get the native path for a mapped file. |
| +bool GetImageFilePath(HANDLE process, |
| + void* base_address, |
| + base::string16* path) { |
| + wchar_t mapped_path[MAX_PATH]; |
| + if (::GetMappedFileNameW(process, base_address, mapped_path, MAX_PATH)) { |
| + *path = mapped_path; |
| + return true; |
| + } |
| + // Image name is potentially greater than MAX_PATH, try larger max size. |
| + std::vector<wchar_t> mapped_path_buffer(SHRT_MAX); |
| + if (::GetMappedFileNameW(process, base_address, &mapped_path_buffer[0], |
| + SHRT_MAX)) { |
| + *path = &mapped_path_buffer[0]; |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| } // namespace |
| namespace sandbox { |
| @@ -410,6 +454,50 @@ DWORD GetLastErrorFromNtStatus(NTSTATUS status) { |
| return NtStatusToDosError(status); |
| } |
| +// This function walks the virtual memory map using VirtualQueryEx to find |
|
Will Harris
2016/09/08 16:54:03
I think some of this comment should be in the .h e
|
| +// the main executable's image section. We attempt to find the first image |
| +// section which matches the path returned for the process. This should only |
| +// be used on suspended processes, and ideally only ones which have not been |
|
Will Harris
2016/09/08 16:54:03
can you do some sort of check that the process is
|
| +// started. There's a slim chance that a process could map its own executables |
| +// file multiple times, but this is pretty unlikely as it's not necessary. |
| +// This shouldn't be a major performance problem because a new process has a |
| +// very limited amount of memory allocated so the majority of the valid range |
| +// should be skipped immediately. However if it turns out to be the case it |
| +// could be optimized in the specific case of the process being the same as the |
| +// current process, which due to ASLR rules the image load address will almost |
| +// always match the current process's load address. |
| +void* GetProcessBaseAddress(HANDLE process) { |
| + MEMORY_BASIC_INFORMATION mem_info = {}; |
| + // Start 64KiB above zero page. |
| + void* current = reinterpret_cast<void*>(0x10000); |
| + base::string16 process_path; |
| + |
| + if (!GetProcessPath(process, &process_path)) |
| + return nullptr; |
| + |
| + // Walk the virtual memory mappings trying to find image sections. |
| + // VirtualQueryEx will return false if it encounters a location outside of |
| + // the user memory range. |
| + while (::VirtualQueryEx(process, current, &mem_info, sizeof(mem_info))) { |
| + base::string16 image_path; |
| + if (mem_info.Type == MEM_IMAGE && |
| + GetImageFilePath(process, mem_info.BaseAddress, &image_path) && |
| + EqualPath(process_path, image_path)) { |
| + return mem_info.BaseAddress; |
| + } |
| + // VirtualQueryEx should fail before overflow, but just in case we'll check |
| + // to prevent an infinite loop. |
| + base::CheckedNumeric<uintptr_t> next_base = |
| + reinterpret_cast<uintptr_t>(mem_info.BaseAddress); |
| + next_base += mem_info.RegionSize; |
| + if (!next_base.IsValid()) |
| + return nullptr; |
| + current = reinterpret_cast<void*>(next_base.ValueOrDie()); |
| + } |
| + |
| + return nullptr; |
| +} |
| + |
| }; // namespace sandbox |
| void ResolveNTFunctionPtr(const char* name, void* ptr) { |
| @@ -423,7 +511,6 @@ void ResolveNTFunctionPtr(const char* name, void* ptr) { |
| // Race-safe way to set static ntdll. |
| ::InterlockedCompareExchangePointer( |
| reinterpret_cast<PVOID volatile*>(&ntdll), ntdll_local, NULL); |
| - |
| } |
| CHECK_NT(ntdll); |