Skip to content

Commit 6b02eb0

Browse files
refactor!: impl a allocator for allocations and unify api
1 parent 4c7fc84 commit 6b02eb0

File tree

16 files changed

+742
-248
lines changed

16 files changed

+742
-248
lines changed

include/blook/allocator.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#pragma once
2+
3+
#include "process.h"
4+
#include "protect.h"
5+
#include <cstdint>
6+
#include <expected>
7+
#include <map>
8+
#include <memory>
9+
#include <optional>
10+
#include <string>
11+
#include <vector>
12+
13+
namespace blook {
14+
class Pointer;
15+
16+
class ProcessAllocator {
17+
public:
18+
explicit ProcessAllocator(std::shared_ptr<Process> proc);
19+
~ProcessAllocator();
20+
21+
CLASS_MOVE_ONLY(ProcessAllocator)
22+
23+
// Allocate memory with optional near address constraint
24+
std::expected<Pointer, std::string>
25+
try_allocate(size_t size, void *nearAddr = nullptr,
26+
Protect protection = Protect::rw);
27+
28+
Pointer allocate(size_t size, void *nearAddr = nullptr,
29+
Protect protection = Protect::rw);
30+
31+
// Deallocate memory
32+
std::expected<void, std::string> try_deallocate(Pointer ptr);
33+
void deallocate(Pointer ptr);
34+
35+
// Get allocation statistics
36+
size_t allocated_count() const;
37+
size_t total_allocated_bytes() const;
38+
size_t total_reserved_bytes() const;
39+
40+
private:
41+
struct AllocationInfo {
42+
size_t size;
43+
Protect protection;
44+
};
45+
46+
struct PageInfo {
47+
void *base_address;
48+
size_t total_size;
49+
size_t used_size;
50+
Protect protection;
51+
std::map<void *, AllocationInfo> allocations;
52+
};
53+
54+
// Direct allocation using VirtualAllocEx
55+
std::expected<void *, std::string>
56+
allocate_direct(size_t size, Protect protection,
57+
void *nearAddr = nullptr);
58+
59+
// Find or create a page suitable for allocation
60+
std::expected<void *, std::string>
61+
find_or_allocate_page(size_t size, void *nearAddr,
62+
Protect protection);
63+
64+
// Allocate a new page
65+
std::expected<void *, std::string>
66+
allocate_new_page(size_t size, void *nearAddr,
67+
Protect protection);
68+
69+
std::shared_ptr<Process> proc;
70+
std::map<void *, PageInfo> pages;
71+
72+
static constexpr size_t DEFAULT_PAGE_SIZE = 64 * 1024; // 64KB
73+
static constexpr size_t LARGE_ALLOCATION_THRESHOLD = 32 * 1024; // 32KB
74+
};
75+
76+
} // namespace blook

include/blook/function.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ class Function {
123123
}
124124

125125
Serializer serializer;
126-
void *pCodePage = Process::self()->malloc(utils::estimateCodeSize(program), Process::MemoryProtection::rwx);
126+
auto pCodePage = Process::self()->malloc(utils::estimateCodeSize(program), Protect::rwx);
127127
if (auto err =
128-
serializer.serialize(program, reinterpret_cast<int64_t>(pCodePage));
128+
serializer.serialize(program, reinterpret_cast<int64_t>((void*)pCodePage));
129129
err != zasm::ErrorCode::None)
130130
throw std::runtime_error(
131131
std::format("JIT Serialization failure: {}", err.getErrorName()));

include/blook/memo.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,17 @@ class Pointer {
4141
friend MemoryPatch;
4242

4343
public:
44-
using MemoryProtection = Process::MemoryProtection;
45-
4644
std::shared_ptr<Process> proc = nullptr;
4745
bool is_self() const;
4846
bool is_valid() const;
4947

5048
bool operator==(const Pointer &other) const = default;
5149

52-
Pointer malloc(size_t size, void *near,
53-
MemoryProtection protection = MemoryProtection::rw);
50+
bool operator==(std::nullptr_t) const { return _offset == 0; }
51+
52+
Pointer malloc(size_t size, void *near, Protect protection = Protect::rw);
5453

55-
Pointer malloc(size_t size,
56-
MemoryProtection protection = MemoryProtection::rw);
54+
Pointer malloc(size_t size, Protect protection = Protect::rw);
5755

5856
Pointer malloc_rx_near_this(size_t size);
5957

@@ -227,14 +225,16 @@ class Pointer {
227225

228226
operator size_t() const { return (size_t)this->_offset; }
229227

230-
inline Pointer absolute(const auto &t) const { return {proc, (void *)t}; }
231-
232-
Function as_function();
228+
operator void *() const { return (void *)this->_offset; }
233229

234-
[[nodiscard]] void *data() const;
230+
[[nodiscard]] inline void *data() const { return (void *)_offset; }
235231

236232
[[nodiscard]] inline size_t offset() const { return _offset; }
237233

234+
inline Pointer absolute(const auto &t) const { return {proc, (void *)t}; }
235+
236+
Function as_function();
237+
238238
// Pointer operations
239239
inline Pointer add(const auto &t) const {
240240
return {proc, (void *)(_offset + (size_t)t)};
@@ -257,7 +257,7 @@ class Pointer {
257257
}
258258

259259
[[nodiscard]] MemoryPatch
260-
reassembly(std::function<void(zasm::x86::Assembler&)>);
260+
reassembly(std::function<void(zasm::x86::Assembler &)>);
261261

262262
[[nodiscard]] MemoryPatch reassembly_thread_pause();
263263

@@ -266,10 +266,10 @@ class Pointer {
266266
// Original location gets a jump to the new memory
267267
[[nodiscard]] std::expected<MemoryPatch, std::string>
268268
try_reassembly_with_trampoline(
269-
std::function<void(zasm::x86::Assembler&)> func);
269+
std::function<void(zasm::x86::Assembler &)> func);
270270

271271
[[nodiscard]] MemoryPatch
272-
reassembly_with_trampoline(std::function<void(zasm::x86::Assembler&)> func);
272+
reassembly_with_trampoline(std::function<void(zasm::x86::Assembler &)> func);
273273

274274
std::optional<Function> guess_function(size_t max_scan_size = 50000);
275275

@@ -296,7 +296,7 @@ class Pointer {
296296
struct ScopedSetMemoryRWX {
297297
Pointer ptr;
298298
size_t size;
299-
Process::MemoryProtection old_protect;
299+
Protect old_protect;
300300

301301
ScopedSetMemoryRWX(Pointer ptr, size_t size);
302302
ScopedSetMemoryRWX(const ScopedSetMemoryRWX &) = delete;
@@ -528,10 +528,10 @@ class MemoryRange : public Pointer {
528528
// If the new code is smaller, pad with NOPs
529529
// If the new code is larger, return unexpected
530530
[[nodiscard]] std::expected<MemoryPatch, std::string>
531-
try_reassembly_with_padding(std::function<void(zasm::x86::Assembler&)> func);
531+
try_reassembly_with_padding(std::function<void(zasm::x86::Assembler &)> func);
532532

533533
[[nodiscard]] MemoryPatch
534-
reassembly_with_padding(std::function<void(zasm::x86::Assembler&)> func);
534+
reassembly_with_padding(std::function<void(zasm::x86::Assembler &)> func);
535535
};
536536

537537
static_assert(std::sentinel_for<decltype(std::declval<MemoryRange>().begin()),

include/blook/process.h

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "platform_types.h"
44
#include "utils.h"
5+
#include "protect.h"
56
#include <cstdint>
67
#include <expected>
78
#include <filesystem>
@@ -18,38 +19,10 @@ class Pointer;
1819
class Module;
1920
struct Thread;
2021
class Process;
21-
22-
using Allocator = struct ProcessAllocator {
23-
explicit ProcessAllocator(std::shared_ptr<Process> proc);
24-
25-
std::optional<Pointer> allocate(size_t size, void *nearAddr = nullptr);
26-
27-
void deallocate(Pointer addr);
28-
29-
private:
30-
struct AllocatedPageData {
31-
size_t size;
32-
std::map<void *, size_t> allocated;
33-
};
34-
std::map<void *, AllocatedPageData> allocatedPages;
35-
std::shared_ptr<Process> proc;
36-
};
22+
class ProcessAllocator;
3723

3824
class Process : public std::enable_shared_from_this<Process> {
3925
public:
40-
enum class MemoryProtection {
41-
None = 0,
42-
Read = 0x0001,
43-
Write = 0x0010,
44-
Execute = 0x0100,
45-
ReadWrite = Read | Write,
46-
ReadWriteExecute = Read | Write | Execute,
47-
ReadExecute = Read | Execute,
48-
rw = ReadWrite,
49-
rwx = ReadWriteExecute,
50-
rx = ReadExecute
51-
};
52-
5326
#ifdef _WIN32
5427

5528
explicit Process(HANDLE h);
@@ -64,9 +37,10 @@ class Process : public std::enable_shared_from_this<Process> {
6437
WIN_ONLY(HANDLE h = nullptr);
6538
friend Module;
6639
friend Pointer;
40+
friend ProcessAllocator;
6741

6842
std::shared_ptr<Pointer> _memo_ptr;
69-
std::shared_ptr<struct ProcessAllocator> _allocator_ptr;
43+
std::shared_ptr<ProcessAllocator> _allocator_ptr;
7044

7145
public:
7246
CLASS_MOVE_ONLY(Process)
@@ -91,23 +65,23 @@ class Process : public std::enable_shared_from_this<Process> {
9165
size_t size) const;
9266
bool check_writable(void *addr, size_t size) const;
9367

94-
std::expected<MemoryProtection, std::string>
68+
std::expected<Protect, std::string>
9569
try_set_memory_protect(void *addr, size_t size,
96-
MemoryProtection protect) const;
97-
MemoryProtection set_memory_protect(void *addr, size_t size,
98-
MemoryProtection protect) const;
70+
Protect protect) const;
71+
Protect set_memory_protect(void *addr, size_t size,
72+
Protect protect) const;
9973

10074
std::expected<bool, std::string> try_check_valid(void *addr) const;
10175
bool check_valid(void *addr) const;
10276

103-
std::expected<void *, std::string>
104-
try_malloc(size_t size, MemoryProtection protect = MemoryProtection::rw,
105-
void *nearAddr = nullptr) const;
106-
void *malloc(size_t size, MemoryProtection protect = MemoryProtection::rw,
107-
void *nearAddr = nullptr) const;
77+
std::expected<Pointer, std::string>
78+
try_malloc(size_t size, Protect protect = Protect::rw,
79+
void *nearAddr = nullptr);
80+
Pointer malloc(size_t size, Protect protect = Protect::rw,
81+
void *nearAddr = nullptr);
10882

109-
std::expected<void, std::string> try_free(void *addr, size_t size = 0) const;
110-
void free(void *addr, size_t size = 0) const;
83+
std::expected<void, std::string> try_free(void *addr, size_t size = 0);
84+
void free(void *addr, size_t size = 0);
11185

11286
[[nodiscard]] std::optional<std::shared_ptr<Module>>
11387
module(const std::string &name);
@@ -126,7 +100,7 @@ class Process : public std::enable_shared_from_this<Process> {
126100

127101
Pointer memo();
128102

129-
struct ProcessAllocator& allocator();
103+
ProcessAllocator& allocator();
130104

131105
std::vector<Thread> threads();
132106

include/blook/protect.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
3+
namespace blook {
4+
enum class Protect {
5+
None = 0,
6+
Read = 0x0001,
7+
Write = 0x0010,
8+
Execute = 0x0100,
9+
ReadWrite = Read | Write,
10+
ReadWriteExecute = Read | Write | Execute,
11+
ReadExecute = Read | Execute,
12+
rw = ReadWrite,
13+
rwx = ReadWriteExecute,
14+
rx = ReadExecute
15+
};
16+
17+
}

src/function.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void *Function::into_safe_function_pointer(void *func, bool thread_safety) {
5454
auto program = Program(utils::compileMachineMode());
5555
auto a = x86::Assembler(program);
5656

57-
auto ptr = Pointer(Process::self(), Process::self()->malloc(300, Process::MemoryProtection::rwx, func));
57+
auto ptr = Process::self()->malloc(300, Protect::rwx, func);
5858
ptr.reassembly([=](auto a) {
5959
#ifdef BLOOK_ARCHITECTURE_X86_64
6060
auto sp = x86::rsp;

src/hook.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ Trampoline Trampoline::make(Pointer pCode, size_t minByteSize,
102102
zasm::Serializer serializer;
103103
auto pCodePage = pCode.malloc(utils::estimateCodeSize(program),
104104
near_alloc ? pCode.data() : nullptr,
105-
Pointer::MemoryProtection::rwx);
105+
Protect::rwx);
106106
if (pCodePage == nullptr) {
107107
throw std::runtime_error("Failed to allocate memory for trampoline.");
108108
}

src/memory.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,6 @@ bool MemoryPatch::restore() {
8383
return true;
8484
}
8585

86-
void *Pointer::data() const { return (void *)_offset; }
87-
8886
bool Pointer::is_self() const { return proc && proc->is_self(); }
8987

9088
std::optional<Function> Pointer::guess_function(size_t max_scan_size) {
@@ -126,7 +124,7 @@ Pointer::find_upwards(std::initializer_list<uint8_t> pattern,
126124
Pointer p = *this;
127125

128126
if (p.proc->is_self())
129-
for (; p > this - max_scan_size; p -= 1) {
127+
for (; p > (*this - max_scan_size); p -= 1) {
130128
if (memcmp(pattern.begin(), (unsigned char *)p.data(), pattern.size()) ==
131129
0) {
132130
return p;

src/platform/linux/memory.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,22 @@ void Pointer::protect_rwx(void *p, size_t size) {
3232

3333
void *Pointer::malloc_near_rwx(void *targetAddr, size_t size) {
3434
return Process::self()->memo().malloc(size, targetAddr,
35-
MemoryProtection::rwx);
35+
Protect::rwx);
3636
}
3737

38-
void *Pointer::malloc(size_t size, Pointer::MemoryProtection protection) {
38+
void *Pointer::malloc(size_t size, Pointer::Protect protection) {
3939
int prot_flags = 0;
4040
switch (protection) {
41-
case MemoryProtection::Read:
41+
case Protect::Read:
4242
prot_flags = PROT_READ;
4343
break;
44-
case MemoryProtection::ReadWrite:
44+
case Protect::ReadWrite:
4545
prot_flags = PROT_READ | PROT_WRITE;
4646
break;
47-
case MemoryProtection::ReadWriteExecute:
47+
case Protect::ReadWriteExecute:
4848
prot_flags = PROT_READ | PROT_WRITE | PROT_EXEC;
4949
break;
50-
case MemoryProtection::ReadExecute:
50+
case Protect::ReadExecute:
5151
prot_flags = PROT_READ | PROT_EXEC;
5252
break;
5353
default:
@@ -62,20 +62,20 @@ void *Pointer::malloc(size_t size, Pointer::MemoryProtection protection) {
6262
Pointer::Pointer(std::shared_ptr<Process> proc) : proc(std::move(proc)) {}
6363

6464
void *Pointer::malloc(size_t size, void *nearby,
65-
Pointer::MemoryProtection protection) {
65+
Pointer::Protect protection) {
6666
long pagesize = sysconf(_SC_PAGESIZE);
6767
int prot_flags = 0;
6868
switch (protection) {
69-
case MemoryProtection::Read:
69+
case Protect::Read:
7070
prot_flags = PROT_READ;
7171
break;
72-
case MemoryProtection::ReadWrite:
72+
case Protect::ReadWrite:
7373
prot_flags = PROT_READ | PROT_WRITE;
7474
break;
75-
case MemoryProtection::ReadWriteExecute:
75+
case Protect::ReadWriteExecute:
7676
prot_flags = PROT_READ | PROT_WRITE | PROT_EXEC;
7777
break;
78-
case MemoryProtection::ReadExecute:
78+
case Protect::ReadExecute:
7979
prot_flags = PROT_READ | PROT_EXEC;
8080
break;
8181
default:

0 commit comments

Comments
 (0)