summaryrefslogtreecommitdiff
path: root/view/sharedcache/core/VirtualMemory.h
blob: 43a9acb4629fabd8271e987d023e5e8e44983b7d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#pragma once
#include "FileAccessorCache.h"
#include "MappedFileAccessor.h"
#include "Utility.h"

class UnmappedRegionException : public std::exception
{
	uint64_t m_address;

public:
	explicit UnmappedRegionException(uint64_t address) : m_address(address) {}

	virtual const char* what() const throw()
	{
		thread_local std::string message;
		message = fmt::format("Tried to access unmapped region using address {0:x}", m_address);
		return message.c_str();
	}
};

// A region within the virtual memory
struct VirtualMemoryRegion
{
	uint64_t fileOffset;
	// Access the memory regions contents through this.
	// NOTE: Any read through this should be seeked to `fileOffset`
	WeakFileAccessor fileAccessor;

	VirtualMemoryRegion(const VirtualMemoryRegion&) = default;

	VirtualMemoryRegion& operator=(const VirtualMemoryRegion&) = default;

	VirtualMemoryRegion(VirtualMemoryRegion&&) = default;

	VirtualMemoryRegion& operator=(VirtualMemoryRegion&&) = default;
};

// Contains information to handle mapping of multiple mapped files into a single memory space.
// This models how the loader of DYLD shared caches would operate, so that we can effectively query memory regions
// and map them into Binary Ninja.
class VirtualMemory
{
	std::shared_mutex m_regionMutex;
	AddressRangeMap<VirtualMemoryRegion> m_regions;

public:
	// At no point do we ever store a strong pointer to a file accessor, that is the job of the `FileAccessorCache`.
	void MapRegion(WeakFileAccessor fileAccessor, AddressRange mappedRange, uint64_t fileOffset);

	// Returns the region in virtual memory, along with the offset into that region where the address is located.
	// Using the regions file accessor and the address offset you can read a regions content.
	std::optional<VirtualMemoryRegion> GetRegionAtAddress(uint64_t address, uint64_t& addressOffset);

	std::optional<VirtualMemoryRegion> GetRegionAtAddress(uint64_t address);

	bool IsAddressMapped(uint64_t address);

	// TODO: Bulk pointer writes here would alleviate a lot of the time spent in the slide info processor.
	// Write a pointer at a given address. This pointer will be persisted
	// for a given `VirtualMemoryRegion` region, unlike using the MappedFileAccessor directly.
	// The persistence is provided through the WeakFileAccessor itself and thus is unique to the construction.
	void WritePointer(size_t address, size_t pointer);

	std::string ReadCString(uint64_t address);

	uint8_t ReadUInt8(uint64_t address);

	int8_t ReadInt8(uint64_t address);

	uint16_t ReadUInt16(uint64_t address);

	int16_t ReadInt16(uint64_t address);

	uint32_t ReadUInt32(uint64_t address);

	int32_t ReadInt32(uint64_t address);

	uint64_t ReadUInt64(uint64_t address);

	int64_t ReadInt64(uint64_t address);

	BinaryNinja::DataBuffer ReadBuffer(uint64_t address, size_t length);

	std::pair<const uint8_t*, const uint8_t*> ReadSpan(size_t address, size_t length);

	void Read(void* dest, uint64_t address, size_t length);
};

class VirtualMemoryReader
{
	std::shared_ptr<VirtualMemory> m_memory;
	uint64_t m_cursor;
	uint64_t m_addressSize;
	BNEndianness m_endianness = LittleEndian;

public:
	explicit VirtualMemoryReader(std::shared_ptr<VirtualMemory> memory, uint64_t addressSize = 8);

	void SetEndianness(BNEndianness endianness) { m_endianness = endianness; }

	BNEndianness GetEndianness() const { return m_endianness; }

	void Seek(const uint64_t address) { m_cursor = address; };

	void SeekRelative(const size_t offset) { m_cursor += offset; };

	size_t GetOffset() const { return m_cursor; }

	std::string ReadCString(uint64_t address, size_t maxLength = -1);

	uint64_t ReadULEB128(size_t cursorLimit);

	int64_t ReadSLEB128(size_t cursorLimit);

	uint64_t ReadPointer();

	uint64_t ReadPointer(uint64_t address);

	uint8_t ReadUInt8();

	uint8_t ReadUInt8(uint64_t address);

	int8_t ReadInt8();

	int8_t ReadInt8(uint64_t address);

	uint16_t ReadUInt16();

	uint16_t ReadUInt16(uint64_t address);

	int16_t ReadInt16();

	int16_t ReadInt16(uint64_t address);

	uint32_t ReadUInt32();

	uint32_t ReadUInt32(uint64_t address);

	int32_t ReadInt32();

	int32_t ReadInt32(uint64_t address);

	uint64_t ReadUInt64();

	uint64_t ReadUInt64(uint64_t address);

	int64_t ReadInt64();

	int64_t ReadInt64(uint64_t address);

	BinaryNinja::DataBuffer ReadBuffer(size_t length);

	BinaryNinja::DataBuffer ReadBuffer(uint64_t address, size_t length);

	void Read(void* dest, size_t length);

	void Read(void* dest, uint64_t address, size_t length);
};