aboutsummaryrefslogtreecommitdiff
path: root/src/memory
diff options
context:
space:
mode:
Diffstat (limited to 'src/memory')
-rw-r--r--src/memory/maps.cc60
-rw-r--r--src/memory/maps.hh62
-rw-r--r--src/memory/memory.cc54
-rw-r--r--src/memory/memory.hh43
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