#include "chrome/chrome_elf/third_party_dlls/hook.h"
#include <windows.h>
#include <winternl.h>
#include <assert.h>
#include <ntstatus.h>
#include <psapi.h>
#include <atomic>
#include <limits>
#include <string>
#include "base/compiler_specific.h"
#include "chrome/chrome_elf/crash/crash_helper.h"
#include "chrome/chrome_elf/pe_image_safe/pe_image_safe.h"
#include "chrome/chrome_elf/sha1/sha1.h"
#include "chrome/chrome_elf/third_party_dlls/hardcoded_blocklist.h"
#include "chrome/chrome_elf/third_party_dlls/logs.h"
#include "chrome/chrome_elf/third_party_dlls/main.h"
#include "chrome/chrome_elf/third_party_dlls/packed_list_file.h"
#include "chrome/chrome_elf/third_party_dlls/packed_list_format.h"
#include "chrome/chrome_elf/third_party_dlls/public_api.h"
#include "sandbox/policy/win/hook_util/hook_util.h"
#include "sandbox/win/src/service_resolver.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
namespace third_party_dlls {
namespace {
typedef ULONG SECTION_INHERIT;
typedef NTSTATUS(WINAPI* NtMapViewOfSectionFunction)(
IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID* BaseAddress,
IN ULONG_PTR ZeroBits,
IN SIZE_T CommitSize,
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
IN OUT PSIZE_T ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Win32Protect);
#pragma section(".crthunk", read, execute)
static __declspec(allocate(".crthunk")) BYTE g_thunk_storage[128];
bool g_hook_active = false;
std::atomic<bool> g_hook_disabled(false);
std::atomic<int32_t> g_apply_hook_result(STATUS_SUCCESS);
bool IsTargetCurrentProcess(HANDLE process) {
return process == ::GetCurrentProcess() ||
::GetProcessId(process) == ::GetCurrentProcessId();
}
bool IsSectionImage(PVOID section_base) {
assert(section_base);
MEMORY_BASIC_INFORMATION info = {};
if (!::VirtualQuery(section_base, &info, sizeof(info))) {
return false;
}
return (info.Type & MEM_IMAGE) == MEM_IMAGE;
}
std::wstring GetSectionName(PVOID section_base) {
assert(section_base);
constexpr DWORD kMaxNameSize = MAX_PATH + 1;
WCHAR name[kMaxNameSize];
DWORD size = ::GetMappedFileName(::GetCurrentProcess(), section_base, name,
kMaxNameSize);
if (size == 0 || size >= kMaxNameSize) {
return std::wstring();
}
return name;
}
bool UTF16ToUTF8(const std::wstring& utf16, std::string* utf8) {
assert(utf8);
if (utf16.empty()) {
utf8->clear();
return true;
}
int size_needed_bytes = ::WideCharToMultiByte(CP_UTF8, 0, &utf16[0],
static_cast<int>(utf16.size()),
nullptr, 0, nullptr, nullptr);
if (!size_needed_bytes)
return false;
utf8->resize(size_needed_bytes);
return ::WideCharToMultiByte(CP_UTF8, 0, &utf16[0],
static_cast<int>(utf16.size()), &(*utf8)[0],
size_needed_bytes, nullptr, nullptr);
}
bool GetDataFromImage(PVOID buffer,
DWORD buffer_size,
DWORD* time_date_stamp,
DWORD* image_size,
std::string* image_name,
std::string* section_path,
std::string* section_basename) {
assert(buffer && buffer_size && time_date_stamp && image_size && image_name &&
section_path && section_basename);
image_name->clear();
section_path->clear();
section_basename->clear();
pe_image_safe::PEImageSafe image(reinterpret_cast<HMODULE>(buffer),
buffer_size);
PIMAGE_FILE_HEADER file_header = image.GetFileHeader();
if (!file_header ||
image.GetImageBitness() == pe_image_safe::ImageBitness::kUnknown) {
return false;
}
*time_date_stamp = file_header->TimeDateStamp;
if (image.GetImageBitness() == pe_image_safe::ImageBitness::k64) {
PIMAGE_OPTIONAL_HEADER64 opt_header =
reinterpret_cast<PIMAGE_OPTIONAL_HEADER64>(image.GetOptionalHeader());
*image_size = opt_header->SizeOfImage;
} else {
PIMAGE_OPTIONAL_HEADER32 opt_header =
reinterpret_cast<PIMAGE_OPTIONAL_HEADER32>(image.GetOptionalHeader());
*image_size = opt_header->SizeOfImage;
}
PIMAGE_EXPORT_DIRECTORY exports = image.GetExportDirectory();
if (exports && exports->Name + MAX_PATH <= buffer_size) {
const char* name =
reinterpret_cast<const char*>(image.RVAToAddr(exports->Name));
*image_name = std::string(name, ::strnlen(name, MAX_PATH));
}
const auto tolower = [](auto c) {
return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
};
for (size_t i = 0; i < image_name->size(); i++)
(*image_name)[i] = tolower((*image_name)[i]);
std::wstring temp_section_path = GetSectionName(buffer);
if (image_name->empty() && temp_section_path.empty())
return false;
if (temp_section_path.empty())
return true;
std::wstring temp_section_basename;
size_t sep = temp_section_path.find_last_of('\\');
if (sep == std::string::npos && !temp_section_path.empty()) {
temp_section_basename = temp_section_path;
} else if (sep != std::string::npos && temp_section_path.back() != '\\') {
temp_section_basename = temp_section_path.substr(sep + 1);
}
for (size_t i = 0; i < temp_section_basename.size(); i++)
temp_section_basename[i] = tolower(temp_section_basename[i]);
return UTF16ToUTF8(temp_section_path, section_path) &&
UTF16ToUTF8(temp_section_basename, section_basename);
}
DISABLE_CFI_ICALL
NTSTATUS NewNtMapViewOfSectionImpl(
NtMapViewOfSectionFunction orig_MapViewOfSection,
HANDLE section,
HANDLE process,
PVOID* base,
ULONG_PTR zero_bits,
SIZE_T commit_size,
PLARGE_INTEGER offset,
PSIZE_T view_size,
SECTION_INHERIT inherit,
ULONG allocation_type,
ULONG protect) {
assert(IsThirdPartyInitialized());
NTSTATUS ret = orig_MapViewOfSection(section, process, base, zero_bits,
commit_size, offset, view_size, inherit,
allocation_type, protect);
if (g_hook_disabled.load(std::memory_order_relaxed))
return ret;
if (!NT_SUCCESS(ret) || !IsTargetCurrentProcess(process) ||
!IsSectionImage(*base)) {
return ret;
}
DWORD time_date_stamp = 0;
DWORD image_size = 0;
std::string image_name;
std::string section_path;
std::string section_basename;
assert(*view_size < std::numeric_limits<DWORD>::max());
if (!GetDataFromImage(*base, static_cast<DWORD>(*view_size), &time_date_stamp,
&image_size, &image_name, §ion_path,
§ion_basename)) {
return ret;
}
elf_sha1::Digest image_name_hash;
if (!image_name.empty())
image_name_hash = elf_sha1::SHA1HashString(image_name);
elf_sha1::Digest section_basename_hash;
if (!section_basename.empty())
section_basename_hash = elf_sha1::SHA1HashString(section_basename);
elf_sha1::Digest fingerprint_hash = elf_sha1::SHA1HashString(
GetFingerprintString(time_date_stamp, image_size));
bool block = false;
if (!image_name.empty() &&
IsModuleListed(image_name_hash, fingerprint_hash)) {
block = true;
} else if (!section_basename.empty() &&
section_basename_hash != image_name_hash &&
IsModuleListed(section_basename_hash, fingerprint_hash)) {
block = true;
} else if (!image_name.empty() && DllMatch(image_name)) {
block = true;
} else if (!section_basename.empty() &&
section_basename.compare(image_name) != 0 &&
DllMatch(section_basename)) {
block = true;
}
if (block) {
::UnmapViewOfFile(*base);
*base = nullptr;
ret = STATUS_UNSUCCESSFUL;
}
LogLoadAttempt((block ? third_party_dlls::LogType::kBlocked
: third_party_dlls::LogType::kAllowed),
image_size, time_date_stamp,
section_path.empty() ? image_name : section_path);
return ret;
}
}
NTSTATUS WINAPI
NewNtMapViewOfSection(NtMapViewOfSectionFunction orig_MapViewOfSection,
HANDLE section,
HANDLE process,
PVOID* base,
ULONG_PTR zero_bits,
SIZE_T commit_size,
PLARGE_INTEGER offset,
PSIZE_T view_size,
SECTION_INHERIT inherit,
ULONG allocation_type,
ULONG protect) {
NTSTATUS ret = STATUS_UNSUCCESSFUL;
__try {
ret = NewNtMapViewOfSectionImpl(
orig_MapViewOfSection, section, process, base, zero_bits, commit_size,
offset, view_size, inherit, allocation_type, protect);
} __except (elf_crash::GenerateCrashDump(GetExceptionInformation())) {
}
return ret;
}
#if defined(_WIN64)
NTSTATUS WINAPI NewNtMapViewOfSection64(HANDLE section,
HANDLE process,
PVOID* base,
ULONG_PTR zero_bits,
SIZE_T commit_size,
PLARGE_INTEGER offset,
PSIZE_T view_size,
SECTION_INHERIT inherit,
ULONG allocation_type,
ULONG protect) {
return NewNtMapViewOfSection(
reinterpret_cast<NtMapViewOfSectionFunction>(g_thunk_storage), section,
process, base, zero_bits, commit_size, offset, view_size, inherit,
allocation_type, protect);
}
#endif
ThirdPartyStatus ApplyHook() {
constexpr wchar_t kNtdllName[] = L"ntdll.dll";
assert(!g_hook_active);
sandbox::ServiceResolverThunk thunk(::GetCurrentProcess(), false);
assert(sizeof(g_thunk_storage) >= thunk.GetThunkSize());
thunk.AllowLocalPatches();
DWORD old_protect = 0;
if (!::VirtualProtect(g_thunk_storage, sizeof(g_thunk_storage),
PAGE_EXECUTE_READWRITE, &old_protect)) {
return ThirdPartyStatus::kHookVirtualProtectFailure;
}
#if defined(_WIN64)
void* entry_point = reinterpret_cast<void*>(&NewNtMapViewOfSection64);
#else
void* entry_point = reinterpret_cast<void*>(&NewNtMapViewOfSection);
#endif
NTSTATUS ntstatus = thunk.Setup(
::GetModuleHandle(kNtdllName), reinterpret_cast<void*>(&__ImageBase),
"NtMapViewOfSection", nullptr, entry_point, g_thunk_storage,
sizeof(g_thunk_storage), nullptr);
if (!NT_SUCCESS(ntstatus)) {
g_apply_hook_result.store(ntstatus, std::memory_order_relaxed);
return ThirdPartyStatus::kHookApplyFailure;
}
if (!::VirtualProtect(g_thunk_storage, sizeof(g_thunk_storage),
PAGE_EXECUTE_READ, &old_protect)) {
assert(false);
}
g_hook_active = true;
return ThirdPartyStatus::kSuccess;
}
bool GetDataFromImageForTesting(PVOID mapped_image,
DWORD* time_date_stamp,
DWORD* image_size,
std::string* image_name,
std::string* section_path,
std::string* section_basename) {
return GetDataFromImage(mapped_image, pe_image_safe::kImageSizeNotSet,
time_date_stamp, image_size, image_name, section_path,
section_basename);
}
}
void DisableHook() {
third_party_dlls::g_hook_disabled.store(true, std::memory_order_relaxed);
}
int32_t GetApplyHookResult() {
return third_party_dlls::g_apply_hook_result.load(std::memory_order_relaxed);
}