An ioctl() call to detect memory writes
An ioctl() call to detect memory writes
Posted Aug 10, 2023 19:27 UTC (Thu) by KJ7RRV (subscriber, #153595)Parent article: An ioctl() call to detect memory writes
Posted Aug 10, 2023 19:39 UTC (Thu)
by dullfire (guest, #111432)
[Link]
Posted Aug 10, 2023 21:28 UTC (Thu)
by comex (subscriber, #71521)
[Link] (3 responses)
The difference is that in the latter case it doesn’t matter how easy the mechanism is to bypass. It might even be acceptable to trivially implement the API to always report that memory has not changed – if that was enough to get games to work. Except it might not be. Some games might be specifically on the lookout for a dummy implementation – not because they had Wine in mind, but because they thought some cheat running natively on Windows might hook GetWriteWatch. So they might deliberately perform a write on some watched memory and verify that it’s reported.
And of course, some Windows applications may be using GetWriteWatch functioning correctly for purposes other than anti-cheat, so a dummy implementation might break them.
Posted Aug 11, 2023 13:48 UTC (Fri)
by stevie-oh (subscriber, #130795)
[Link] (2 responses)
Java and .NET are a garbage-collected languages. This means that memory is not explicitly freed by code that executes in the JVM/CLR. Instead, whenever the process starts to run low on memory, everything is paused and the garbage collector is run in order to free memory no longer in use.
Determining which memory is in use is straightforward:
- First, there are all the "roots": the memory immediately accessible to any thread in the process. To access memory, (valid) code needs a reference to it. Such references will necessarily live inside the current CPU registers of threads, variables on threads' stacks, and global variables.
From there, that memory can contain references to other memory. The GC (garbage collector) traces through all of these references recursively and keeps track of all memory that it can reach. Any memory it _doesn't_ reach, therefore, is free.
One inefficient thing about this is the global variables. Those tend to be static data that was initialized at (or near) program startup; they never change. But every time memory reclaim is needed, the GC needs to check through all those objects again.
However, there's a trick here: old allocations can't refer to newer allocations unless they've been updated. So the .NET GC uses GetWriteWatch on older allocations as a optimization: If the old allocations haven't been written, then it knows these things:
- all memory that was reachable from those older allocations is still around. don't free it.
Posted Aug 11, 2023 14:05 UTC (Fri)
by paulj (subscriber, #341)
[Link]
Each successive heap has a longer and longer scan time. I.e., X_0 < X_1 < ..., and X_i < X_(i+1) for all i in N_0. So young objects get checked quickly, longer lived objects get checked less and less.
Posted Aug 11, 2023 17:41 UTC (Fri)
by Cyberax (✭ supporter ✭, #52523)
[Link]
.NET used to use the dirty bits to track the changes, but they switched to card marking ( https://mattwarren.org/2016/02/04/learning-how-garbage-co... ) a while ago. It turns out that games with virtual memory are slow, and the page-level granularity is a bit too big.
They have never used GetWriteWatch though, but something different. There's a way in Windows to query a list of pages that have a "dirty" bit set, but the function name eludes me right now.
An ioctl() call to detect memory writes
Or worse case, a patched ld.so (and an strace run to identify raw call sites)
An ioctl() call to detect memory writes
An ioctl() call to detect memory writes
- no newer allocations can possibly be referenced and thus be kept alive by those older allocations.
An ioctl() call to detect memory writes
An ioctl() call to detect memory writes