Reputation: 73
I'm sure this is a super weird question, but believe me, it has a point. I have an array of values in GameMaker Language, and I want to be able to search the array for values outside of the array's bounds, and retrieve those out-of-bounds values rather than returning an error and stopping the game. Basically, I want to intentionally pull incorrect data from nearby memory when searching the array with an out-of-bounds index. Is this possible? Thank you in advance!
Upvotes: 0
Views: 406
Reputation: 3202
but believe me, it has a point
It is good to mention why you want to do something so that if that cannot be done, recommendations can be given without follow-up question(s).
GameMaker does not allow invalid memory access (save for exotic bugs), though even if it did, there would be more to this - if you inspect YYGML.h (in the runtime directory), GML values are represented by struct RValue
, which is a 16-byte structure consisting of 8 value bytes (such as a numeric value, a string pointer, an array pointer, or so on), value type index, and metadata flags. In other words, attempting to read arbitrary memory as a GML value would not work since both type and flags would be garbage, most likely causing the runtime to crash with an access violation the moment you hit a pointer-typed value pointing at an invalid memory location.
If you would like to read arbitrary memory within your process, you could write a native extension (usually in C++) with a function that takes a buffer address (from buffer_get_address
), starting address, and length, copying the bytes to the buffer by calling VirtualQuery
(to validate that memory region is accessible and retrieve bounds) and subsequently memcpy
.
A non-GML specific implementation of such a function may look like this:
bool safeCopy(byte* out, const byte* from, size_t size) {
const auto pmask = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE |
PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
auto at = from;
auto till = at + size;
::MEMORY_BASIC_INFORMATION mbi{};
while (at < till) {
if (!VirtualQuery(at, &mbi, sizeof mbi)) return false;
if ((mbi.State == MEM_COMMIT) && !(mbi.Protect & PAGE_GUARD) && (mbi.Protect & pmask)) {
const byte* m_begin = static_cast<const byte*>(mbi.BaseAddress);
const byte* m_end = m_begin + mbi.RegionSize;
at = m_end;
if (m_end > till) m_end = till;
if (m_begin < from) m_begin = from;
memcpy(out + (m_begin - from), m_begin, m_end - m_begin);
} else return false;
}
return true;
}
Upvotes: 1