|
|
Subscribe / Log in / New account

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

Couldn't the anti-cheat still be defeated with a patched kernel?


to post comments

An ioctl() call to detect memory writes

Posted Aug 10, 2023 19:39 UTC (Thu) by dullfire (guest, #111432) [Link]

you could probably defeat it with a an LD_PRELOAD.
Or worse case, a patched ld.so (and an strace run to identify raw call sites)

An ioctl() call to detect memory writes

Posted Aug 10, 2023 21:28 UTC (Thu) by comex (subscriber, #71521) [Link] (3 responses)

I’m purely speculating, but I suspect the goal is less ‘help anti-cheat software to actually detect cheats’, more ‘get existing anti-cheat software designed for Windows to stop producing false positives’.

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.

An ioctl() call to detect memory writes

Posted Aug 11, 2023 13:48 UTC (Fri) by stevie-oh (subscriber, #130795) [Link] (2 responses)

Yeah, when I first read about GetWriteWatch, my thought wasn't anti-cheat. It's similar to (but distinct from) the emulator case.

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.
- no newer allocations can possibly be referenced and thus be kept alive by those older allocations.

An ioctl() call to detect memory writes

Posted Aug 11, 2023 14:05 UTC (Fri) by paulj (subscriber, #341) [Link]

Some GCs maintain "generations" of heaps. Allocations start in the 0th generation heap. If they have not been freed after X_0 GC scans of that heap they are migrated to the 1st-gen heap; if not freed after X_1 scans, migrate to 2nd-gen heap; and so on.

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.

An ioctl() call to detect memory writes

Posted Aug 11, 2023 17:41 UTC (Fri) by Cyberax (✭ supporter ✭, #52523) [Link]

> 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

.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.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds