mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-17 17:27:19 +02:00
Render-resource pool: the other renderer is allocated room for its metadata pointers
719cba1 stopped writing the metadata pointers into the other renderer
(0x2daa00 bytes, Skate.exe+0x30c64cc, kept at 0x75f2700), taking them for
the state owner's alone. They are not: the other renderer's own code reads
them through the patched view (0x030e6bff, called from 0x30a7e18) and
append (0x03e94fe6, called from 0x30d8ebb). Without the write, it read
whatever lay 18 MB past its end. With it, as in every release before, that
memory was overwritten, or the write faulted when nothing was mapped there
(the store_field crash).
- The other renderer's allocation is patched to the state owner's
0x11ab080 bytes (0x030c64cc, mov ecx), so +0x11aae28/+0x11aae30 are
inside it.
- Both renderers' metadata pointers are written again, before and after
the native constructor. The resource-state and relation-mask vectors stay
the state owner's alone: the other renderer keeps fields of its own at
+0x2da920.
- The contract's comment says which renderer reads what, and where.
This commit is contained in:
1 parent
719cba1166
commit
3faa58bb42
2 files changed
+21
-15
No files matched your search
@@ -29,6 +29,9 @@ inline constexpr auto constructor_entry = hex_bytes(
|
||||
inline constexpr auto index_table_constructor_entry = hex_bytes(
|
||||
"4053555657415641574883ec78c5f9efc033ed488bf948896c2428488d4c2440");
|
||||
|
||||
// mov ecx, 0x2daa00 -> mov ecx, 0x11ab080 (the state owner's size, 0x3efd347).
|
||||
inline constexpr auto other_renderer_size_before = hex_bytes("b900aa2d00");
|
||||
inline constexpr auto other_renderer_size_after = hex_bytes("b980b01a01");
|
||||
inline constexpr auto vector_begin_before = hex_bytes("488d81b0000000");
|
||||
inline constexpr auto vector_begin_after = hex_bytes("488b8190000000");
|
||||
inline constexpr auto vector_bytes_before = hex_bytes("480500001e00");
|
||||
@@ -92,15 +95,20 @@ inline constexpr std::ptrdiff_t relation_mask_vector_offset = 0x114ab60;
|
||||
inline constexpr std::uintptr_t constructor_rva = 0x03e7f300;
|
||||
// The constructor is the base of two renderers. Only the one constructed here
|
||||
// (vtable 0x64f2c10, 0x11ab080 bytes) has the resource-state and relation-mask
|
||||
// vectors and the metadata fields: every read of owner+0x2da920, owner+0x114ab60
|
||||
// and owner+0x11aae28 is in its code. The other (constructed at 0x030a0a46,
|
||||
// vtable 0x6414168) is 0x2daa00 bytes (Skate.exe+0x30c64cc): it keeps fields of
|
||||
// its own at +0x2da920, and the rest are past its end.
|
||||
// vectors: every read of owner+0x2da920 and owner+0x114ab60 is in its code. The
|
||||
// other (constructed at 0x030a0a46, vtable 0x6414168, kept at 0x75f2700) keeps
|
||||
// fields of its own at +0x2da920. Both use the metadata: the patched view
|
||||
// (0x030e6bff, called from 0x30a7e18 with the other renderer) and append
|
||||
// (0x03e94fe6, called from 0x30d8ebb) read its pointers at +0x11aae28/+0x11aae30.
|
||||
// The other renderer was allocated 0x2daa00 bytes, which ends before them, so it
|
||||
// is allocated the state owner's 0x11ab080 instead (other_renderer_size, above).
|
||||
inline constexpr std::uintptr_t state_owner_call_rva = 0x03f33a46;
|
||||
inline constexpr std::uintptr_t index_table_constructor_rva = 0x03e7e400;
|
||||
inline constexpr std::uintptr_t resource_flags_priority_rva = 0x03f56073;
|
||||
inline constexpr std::uintptr_t resource_flags_visibility_rva = 0x03f5789a;
|
||||
inline constexpr Patch patches[]{
|
||||
// The other renderer's allocation, large enough for the metadata pointers.
|
||||
{0x030c64cc, other_renderer_size_before, other_renderer_size_after},
|
||||
{0x03e7f33c, vector_begin_before, vector_begin_after},
|
||||
{0x03e7f351, vector_bytes_before, vector_bytes_after},
|
||||
{0x03e94e99, capacity_before, capacity_after},
|
||||
|
||||
@@ -308,13 +308,13 @@ void* __fastcall construct_native_renderer(void* self, void* configuration) {
|
||||
// The patched constructor loads its initial vector pointer from +0x90,
|
||||
// constructs the native default record there, and advances +0x98 itself.
|
||||
store_field(self, vector_begin_offset, buffers.records);
|
||||
// The metadata fields, like the state vectors, are the state owner's alone: the other
|
||||
// renderer is 0x2daa00 bytes (Skate.exe+0x30c64cc), and +0x11aae28 is 18 MB past its end.
|
||||
// Written there for every launch until 2026-10-15, they went into whatever memory lay there
|
||||
// (a crash in store_field when it was not mapped).
|
||||
// Both renderers read the metadata through these pointers (render_resource_pool.h). The other
|
||||
// renderer was allocated 0x2daa00 bytes, and they were written 18 MB past its end, into
|
||||
// whatever lay there (a crash in store_field when nothing did): it is allocated the state
|
||||
// owner's size now (the patch at 0x030c64cc), so they are its own.
|
||||
store_field(self, metadata_begin_offset, buffers.metadata);
|
||||
store_field(self, metadata_end_offset, metadata_end);
|
||||
if (states) {
|
||||
store_field(self, metadata_begin_offset, buffers.metadata);
|
||||
store_field(self, metadata_end_offset, metadata_end);
|
||||
store_field(self, resource_state_vector_offset, buffers.resource_states);
|
||||
store_field(self, relation_mask_vector_offset, relation_mask_begin);
|
||||
}
|
||||
@@ -323,11 +323,9 @@ void* __fastcall construct_native_renderer(void* self, void* configuration) {
|
||||
|
||||
// The side-array fields are initialized by later renderer setup on the
|
||||
// stock build. Keep the external values authoritative across construction.
|
||||
if (states) {
|
||||
store_field(self, metadata_begin_offset, buffers.metadata);
|
||||
store_field(self, metadata_end_offset, metadata_end);
|
||||
state_owner.store(reinterpret_cast<std::uintptr_t>(self), std::memory_order_release);
|
||||
}
|
||||
store_field(self, metadata_begin_offset, buffers.metadata);
|
||||
store_field(self, metadata_end_offset, metadata_end);
|
||||
if (states) state_owner.store(reinterpret_cast<std::uintptr_t>(self), std::memory_order_release);
|
||||
if (load_field<std::byte*>(self, vector_begin_offset) != buffers.records ||
|
||||
load_field<std::byte*>(self, vector_capacity_offset) != record_end ||
|
||||
load_field<std::byte*>(self, vector_end_offset) < buffers.records ||
|
||||
|
||||
Reference in new issue
Block a user