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); |