diff options
Diffstat (limited to 'src/memory')
| -rw-r--r-- | src/memory/maps.cc | 60 | ||||
| -rw-r--r-- | src/memory/maps.hh | 62 | ||||
| -rw-r--r-- | src/memory/memory.cc | 54 | ||||
| -rw-r--r-- | src/memory/memory.hh | 43 |
4 files changed, 219 insertions, 0 deletions
diff --git a/src/memory/maps.cc b/src/memory/maps.cc new file mode 100644 index 0000000..290675a --- /dev/null +++ b/src/memory/maps.cc @@ -0,0 +1,60 @@ +#include "memory/maps.hh" + +namespace memory { + +static std::string get_pid_from_name(const std::string& name) { + for (const auto& pid : std::filesystem::directory_iterator("/proc")) { + if (!pid.is_directory()) { + continue; + } + + std::ifstream file{std::string{pid.path()} + "/comm"}; + if (!file.is_open()) { + continue; + } + const std::string contents{std::istreambuf_iterator<char>{file}, + std::istreambuf_iterator<char>{}}; + if (!contents.starts_with(name)) { + continue; + } + + const std::string pathname = pid.path(); + const auto pos = static_cast<long>(pathname.find_last_of('/')); + return {std::next(std::begin(pathname) + pos), std::end(pathname)}; + } + + throw std::runtime_error{"failed to get pid from name \"" + name + + "\", is it open?"}; +} + +maps::maps(const char* const name) : pid(get_pid_from_name(name)) { + std::ifstream file{"/proc/" + this->pid + "/maps"}; + if (!file.is_open()) { + throw std::runtime_error{"failed to open maps for pid \"" + this->pid + + '\"'}; + } + std::stringstream file_ss; + file_ss << file.rdbuf(); + for (std::string line; std::getline(file_ss, line);) { + if (line.empty()) { + continue; + } + + std::stringstream line_ss{line}; + + // address perms offset dev inode pathname + // 00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/dbus-daemon + maps::region region; + line_ss >> region.start; + line_ss.ignore(); // ignore dash + line_ss >> region.end; + line_ss >> region.perms; + line_ss >> region.offset; + line_ss >> region.dev; + line_ss >> region.inode; + line_ss >> region.pathname; + this->regions.push_back(std::move(region)); + } +} + +} // namespace memory diff --git a/src/memory/maps.hh b/src/memory/maps.hh new file mode 100644 index 0000000..67914b6 --- /dev/null +++ b/src/memory/maps.hh @@ -0,0 +1,62 @@ +#ifndef MEMORY_HH_ +#define MEMORY_HH_ + +#include <filesystem> +#include <fstream> +#include <sstream> +#include <stdexcept> +#include <string> +#include <vector> + +namespace memory { + +class maps { +public: + struct region { + public: + void* start; + void* end; + char perms[5]; + std::string offset; // TODO who cares + std::string dev; + int inode; + std::string pathname; + + public: + region() noexcept = default; + region(const region&) = default; + region(region&&) = default; + + public: + bool is_read() const noexcept { return this->perms[0] == 'r'; } + bool is_write() const noexcept { return this->perms[1] == 'w'; } + bool is_execute() const noexcept { return this->perms[2] == 'x'; } + bool is_protected() const noexcept { return this->perms[3] == 'p'; } + }; + using regions_t = std::vector<region>; + +private: + std::string pid; + regions_t regions; + +public: + auto begin() const noexcept { + return std::begin(this->regions); + } + auto end() const noexcept { + return std::end(this->regions); + } + +public: + maps(const char* const name); + maps(const maps& m) = default; + maps(maps&&) = default; + +public: + const std::string& get_pid() const noexcept { return this->pid; } + const regions_t& get_regions() const noexcept { return this->regions; } +}; + +} // namespace memory + +#endif diff --git a/src/memory/memory.cc b/src/memory/memory.cc new file mode 100644 index 0000000..59fb50f --- /dev/null +++ b/src/memory/memory.cc @@ -0,0 +1,54 @@ +#include "memory/memory.hh" + +namespace memory { + +buffer_t read_buffer(const std::size_t num_bytes, const pid_t& pid, + const void* const address) { + buffer_t buffer{num_bytes}; + const struct iovec local { + .iov_base = std::data(buffer), .iov_len = std::size(buffer) + }; + const struct iovec remote { + .iov_base = const_cast<void* const>(address), .iov_len = num_bytes + }; + + if (const auto read = process_vm_readv(pid, &local, 1, &remote, 1, 0); + static_cast<unsigned long>(read) != num_bytes) { + + if (read == -1) { + throw std::runtime_error{"read fail - " + + std::string{strerror(errno)}}; + } + throw std::runtime_error("read fail - not enough bytes read (" + + std::to_string(num_bytes) + " wanted, " + + std::to_string(read) + " read)"); + } + + return buffer; +} + +void write_buffer(const buffer_t& buffer, const pid_t& pid, + const void* const address) { + const struct iovec local { + .iov_base = (void*)std::data(buffer), .iov_len = std::size(buffer) + }; + const struct iovec remote { + .iov_base = const_cast<void* const>(address), + .iov_len = std::size(buffer) + }; + + if (const auto written = process_vm_writev(pid, &local, 1, &remote, 1, 0); + static_cast<unsigned long>(written) != std::size(buffer)) { + + if (written == -1) { + throw std::runtime_error{"write fail - " + + std::string{strerror(errno)}}; + } + throw std::runtime_error("read fail - not enough bytes written (" + + std::to_string(std::size(buffer)) + + " wanted, " + std::to_string(written) + + " written)"); + } +} + +} // namespace memory diff --git a/src/memory/memory.hh b/src/memory/memory.hh new file mode 100644 index 0000000..c35feea --- /dev/null +++ b/src/memory/memory.hh @@ -0,0 +1,43 @@ +#ifndef MEMORY_MEMORY_HH_ +#define MEMORY_MEMORY_HH_ + +#include <cstddef> +#include <cstring> +#include <sys/uio.h> +#include <vector> +#include <stdexcept> + +namespace memory { + +using buffer_t = std::vector<std::byte>; + +buffer_t read_buffer(const std::size_t num_bytes, const pid_t& pid, + const void* const address); +void write_buffer(const buffer_t& buffer, const pid_t& pid, + const void* const address); + +template <typename T> +T read(const pid_t& pid, const void* const address) { + static_assert(std::is_trivially_copyable<T>::value); + + const buffer_t data = read_buffer(sizeof(T), pid, address); + T ret; + std::memcpy(&ret, std::data(data), sizeof(T)); + return std::move(ret); +} + +template <typename T> +void write(const T& data, const pid_t& pid, const void* const address) { + static_assert(std::is_trivially_copyable<T>::value); + + const buffer_t buffer = [&]() { + buffer_t buffer{sizeof(T)}; + std::memcpy(std::data(buffer), &data, sizeof(T)); + return buffer; + }(); + write_buffer(buffer, pid, address); +} + +} // namespace memory + +#endif |
