Actually they do. Without going into too much detail, they send a 'loaded module' list to the server on GM request. On Aradune, this is still enabled as automatic - and I can reliably see the message appear after being logged in for 5 minutes. If I set the 'detected' flag to true after receiving a specific response from the server, it will crash. By editing the processes' running memory to set that flag to true, the game will crash. You won't see it in packet collects, as it is xor'd to prevent reading the plaintext data.
The same packet includes information about if your client is detected running in a VM, a crc32mem of the text in question (crc32mem has been used for years in the EQ client for other anti-cheat purposes, like making sure you're using a eqgame.exe/spells_us.txt that matches the server), and crc32mem of a few hardware components - the Launchpad.exe program also records this data for analytics purposes. The three together make a 'fingerprint' for the client.
The modules that get loaded outside of the game additionally show up. For example, if you have a Discord overlay enabled, that will be seen by them on the server side.
If you remember a few years back, they had the list embedded into the client of stuff they check for. Now it's checked on the serverside instead of also being exposed on the client... and it's now XOR'd.
This does NOT account for external programs like MySEQ which only read memory and exist in a different process space. But MQ2 is stupidly easy to detect and makes no attempt to hide itself.
This crc32mem function existed in clients dating back to 2003; it's quite old, but reliable.
View attachment 346325
In the 2008 testeqgame.map, we can see a clear view of where that function is used:
View attachment 346326
Even back in 2008, they had tried to prevent some MQ2 functionality. You all might remember that 'stop cheating' warning they put out around that time for ghostkill, that was ominous and said, "We know who you are."
Well, this is how they knew: they hashed the memory versus the value stored in WinMain. MQ2 would sometimes not inject itself until the game was fully loaded, and this would work most of the time to detect memory hooks:
View attachment 346327
View attachment 346328
The above is still in the client. I'm surprised they haven't utilized it. Or maybe they do. Who knows.