Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ set(LIBHAT_SRC
src/Scanner.cpp
src/System.cpp

src/os/mac/MemoryProtector.cpp
src/os/mac/Process.cpp

src/os/linux/MemoryProtector.cpp
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,13 @@ Below is a summary of the current support for libhat's platform-dependent APIs:
| | Windows | Linux | macOS | Android |
|-----------------------------------|:-------:|:-----:|:-----:|:-------:|
| `hat::get_system` | ✅ | ✅ | ✅ | ✅ |
| `hat::memory_protector` | ✅ | ✅ | | ✅ |
| `hat::memory_protector` | ✅ | ✅ | | ✅ |
| `hp::get_process_module` | ✅ | ✅ | ✅ | ✅ |
| `hp::get_module` | ✅ | ✅ | ✅ | ✅ |
| `hp::module_at` | ✅ | | | |
| `hp::is_readable` | ✅ | ✅ | | ✅ |
| `hp::is_writable` | ✅ | ✅ | | ✅ |
| `hp::is_executable` | ✅ | ✅ | | ✅ |
| `hp::module_at` | ✅ | | | |
| `hp::is_readable` | ✅ | ✅ | | ✅ |
| `hp::is_writable` | ✅ | ✅ | | ✅ |
| `hp::is_executable` | ✅ | ✅ | | ✅ |
| `hp::module::get_module_data` | ✅ | ✅ | ✅ | ✅ |
| `hp::module::get_executable_data` | ✅ | ✅ | ✅ | ✅ |
| `hp::module::get_section_data` | ✅ | ✅ | ✅ | ✅ |
Expand Down
21 changes: 5 additions & 16 deletions src/os/linux/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,22 +274,11 @@ namespace hat::process {
}

std::optional<hat::process::module> module_at(void* address, [[maybe_unused]] const std::optional<size_t> size) {
std::optional<hat::process::module> module{};
auto callback = [&](const dl_phdr_info& info) {
if (std::bit_cast<void*>(info.dlpi_addr) == address) {
module = hat::process::module{std::bit_cast<uintptr_t>(info.dlpi_addr)};
return 1;
}
return 0;
};

dl_iterate_phdr(
[](dl_phdr_info* info, size_t, void* data) {
return (*static_cast<decltype(callback)*>(data))(*info);
},
&callback);

return module;
Dl_info info{};
if (dladdr(address, &info)) {
return hat::process::module{std::bit_cast<uintptr_t>(info.dli_fbase)};
}
return std::nullopt;
}

bool is_readable(const std::span<const std::byte> region) {
Expand Down
72 changes: 72 additions & 0 deletions src/os/mac/MemoryProtector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include <libhat/defines.hpp>
#if defined(LIBHAT_MAC) && defined(LIBHAT_LP64)

#include <libhat/memory_protector.hpp>

#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <mach/vm_prot.h>

#include <optional>

namespace hat {

static std::optional<uint32_t> get_page_prot(const uintptr_t address) {
mach_vm_address_t addr = static_cast<mach_vm_address_t>(address);
mach_vm_size_t size{};
vm_region_basic_info_64 info{};
mach_msg_type_number_t infoCount = VM_REGION_BASIC_INFO_COUNT_64;
mach_port_t objectName{};

const auto result = mach_vm_region(
mach_task_self(),
&addr,
&size,
VM_REGION_BASIC_INFO_64,
reinterpret_cast<vm_region_info_t>(&info),
&infoCount,
&objectName
);

if (result != KERN_SUCCESS) {
return std::nullopt;
}

return static_cast<uint32_t>(info.protection);
}

static vm_prot_t to_system_prot(const protection flags) {
vm_prot_t prot = 0;
if (static_cast<bool>(flags & protection::Read)) prot |= VM_PROT_READ;
if (static_cast<bool>(flags & protection::Write)) prot |= VM_PROT_WRITE;
if (static_cast<bool>(flags & protection::Execute)) prot |= VM_PROT_EXECUTE;
return prot;
}

memory_protector::memory_protector(const uintptr_t address, const size_t size, const protection flags) : address(address), size(size) {
const auto oldProt = get_page_prot(address);
if (!oldProt) {
return; // Failure indicated via is_set()
}

this->oldProtection = *oldProt;
this->set = KERN_SUCCESS == mach_vm_protect(
mach_task_self(),
static_cast<mach_vm_address_t>(this->address),
static_cast<mach_vm_size_t>(this->size),
false,
to_system_prot(flags)
);
}

void memory_protector::restore() {
mach_vm_protect(
mach_task_self(),
static_cast<mach_vm_address_t>(this->address),
static_cast<mach_vm_size_t>(this->size),
false,
static_cast<vm_prot_t>(this->oldProtection)
);
}
}
#endif
52 changes: 52 additions & 0 deletions src/os/mac/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <mach/vm_prot.h>

#include <dlfcn.h>
Expand Down Expand Up @@ -91,6 +93,48 @@ namespace hat::process {
});
}

#ifdef LIBHAT_LP64
static bool regionHasFlags(const std::span<const std::byte> region, const vm_prot_t flags) {
for (auto* addr = region.data(); addr < region.data() + region.size();) {
mach_vm_address_t vm_addr = std::bit_cast<mach_vm_address_t>(addr);
mach_vm_size_t size{};
vm_region_basic_info_64 info{};
mach_msg_type_number_t infoCount = VM_REGION_BASIC_INFO_COUNT_64;
mach_port_t objectName{};

const auto result = mach_vm_region(
mach_task_self(),
&vm_addr,
&size,
VM_REGION_BASIC_INFO_64,
reinterpret_cast<vm_region_info_t>(&info),
&infoCount,
&objectName
);
if (result != KERN_SUCCESS) {
return false;
}
if (!(info.protection & flags)) {
return false;
}
addr = std::bit_cast<const std::byte*>(vm_addr) + size;
}
return true;
}

bool is_readable(const std::span<const std::byte> region) {
return regionHasFlags(region, VM_PROT_READ);
}

bool is_writable(const std::span<const std::byte> region) {
return regionHasFlags(region, VM_PROT_WRITE);
}

bool is_executable(const std::span<const std::byte> region) {
return regionHasFlags(region, VM_PROT_EXECUTE);
}
#endif

hat::process::module get_process_module() {
const uint32_t count = _dyld_image_count();
for (uint32_t i = 0; i != count; i++) {
Expand Down Expand Up @@ -230,6 +274,14 @@ namespace hat::process {

return {};
}

std::optional<hat::process::module> module_at(void* address, [[maybe_unused]] const std::optional<size_t> size) {
Dl_info info{};
if (dladdr(address, &info)) {
return hat::process::module{std::bit_cast<uintptr_t>(info.dli_fbase)};
}
return std::nullopt;
}
}

#endif