Recently bought this game and started playing, noticing it's protected by EAC. However, there's the possibility to run the game without it. But.. achievements won't trigger on various events if EAC is off. Been reading here and there for some solutions, before I'd go down into it (makes no sense to do anything if someone's already achieved this) and found a post where someone claimed they've got some loader that allows achievements to pipe through while EAC is disabled. That kinda started me thinking a bit: of course developers would want to see if achievements work without EAC on. If achievements were available only when EAC is enabled, then they wouldn't be able to debug-test them. So there should've been some quick, "dirty" way. You'll learn in a bit what that trick is.
Now.. I've been looking for a while now into emulating both the x86 and x64 EAC DLLs for single-player purposes. That would basically eliminate the need for the launchers you've seen with most trainer sites. You'd need just the DLLs alone replaced in the game_folder\EasyAntiCheat_folder.
Back to MCC.. I was interested in 3 components: the UE4-built GUI .exe (MCC-Win64-Shipping.exe), the engine .dll for each of the games in the series and EAC itself. Similarly, my interest in EAC targeted 3 out of 4 major components that are exposed: mcclauncher.exe (launches the anti-cheat, then the game), EasyAntiCheat_x86.dll and EasyAntiCheat_x64.dll. The 4th component isn't quite visible from starters; and that is the .sys driver the x86 .dll downloads on initialization, unpacks, loads up, hooks ObCallbacks to disallow system-wide driver installing, etc. The core of their protection. Lots of reading will help understand what's going under the hood.
The first thing I did in my quest was to see if replacing the x64 .dll with the one I wrote for Wildlands still works. In the sense that I was curious if the current .dll has more exports exposed than the one I used at the time. Surprise, the DLL works, but that isn't sufficient. First-up, the x86 .dll which loads the driver will also hash out all its dependencies. That's where the first fail occurs. Reporting that the x64 .dll is "corrupted". If I manually and directly load MCC-Win64-Shipping.exe, it will skip mcclauncher.exe. Then it will load the EAC x64 library, as there's an initialization function right here:
(you can use AOBs to find it.. or just look for 'EasyAntiCheat' in the string references)
So with that in mind I played 1-2 Reach missions and saw no pop-ups from Steam upon completion. There are achievements for finishing each of the missions in the campaign. Considering I've basically 'bypassed' EAC and all's green, I wondered why nothing happened. So I went back to the code above.
After loading the x64 .dll there's this piece of code:
Code: Select all
00007FF7B1D2CAE8 | 48:8D15 D1682001 | LEA RDX,QWORD PTR DS:[7FF7B2F333C0] | 00007FF7B2F333C0:"CreateGameClient" 00007FF7B1D2CAEF | 48:8BC8 | MOV RCX,RAX | 00007FF7B1D2CAF2 | FF15 E08EAF00 | CALL QWORD PTR DS:[<&GetProcAddress>] | 00007FF7B1D2CAF8 | 48:8D0D B9672001 | LEA RCX,QWORD PTR DS:[7FF7B2F332B8] | 00007FF7B2F332B8:"GameClientInterfaceV012" 00007FF7B1D2CAFF | FFD0 | CALL RAX |
Code: Select all
00007FF9FFCE5B51 | 48:8BD7 | MOV RDX,RDI | rdi:"GameClientInterfaceV012" 00007FF9FFCE5B54 | B9 AC0E0000 | MOV ECX,EAC | 00007FF9FFCE5B59 | FF15 B1390600 | CALL QWORD PTR DS:[<&GetProcAddress>] | 00007FF9FFCE5B5F | 48:8BD8 | MOV RBX,RAX | 00007FF9FFCE5B62 | 48:85C0 | TEST RAX,RAX | 00007FF9FFCE5B65 | 75 1E | JNE easyanticheat_x64.7FF9FFCE5B85 | 00007FF9FFCE5B67 | FF15 6B360600 | CALL QWORD PTR DS:[<&GetCommandLineW>] | 00007FF9FFCE5B6D | 48:8BC8 | MOV RCX,RAX | 00007FF9FFCE5B70 | E8 6BC6FEFF | CALL easyanticheat_x64.7FF9FFCD21E0 |<-- enter here 00007FF9FFCE5B75 | 48:85C0 | TEST RAX,RAX |
So it checks if the game has been launched with -eac-nop-loaded command-line. Cool. So I then started the game with this command-line and and replayed the first Reach mission. What do you know, upon completion, I got the achievement. Aha, so that's the developers' trick to quickly test out the achievement system while EAC is disabled Naughty naughty.
So that's one way of doing it.
Then I was curious even further: what's the actual trigger in-game that tells the game EAC has been initialized or not? How does the game know, when attempting to unlock an achievement, that EAC is disabled? The answer came a few minutes later.
What I did was to continue tracing the code past the -eac-nop-loaded processing part. Won't detail the whole interface creation process, but just the content of the structure and what I noticed. Exiting the "CreateGameClient" function there's this write here:
Code: Select all
00007FF7B1D2CAE8 | 48:8D15 D1682001 | LEA RDX,QWORD PTR DS:[7FF7B2F333C0] | 00007FF7B2F333C0:"CreateGameClient" 00007FF7B1D2CAEF | 48:8BC8 | MOV RCX,RAX | 00007FF7B1D2CAF2 | FF15 E08EAF00 | CALL QWORD PTR DS:[<&GetProcAddress>] | 00007FF7B1D2CAF8 | 48:8D0D B9672001 | LEA RCX,QWORD PTR DS:[7FF7B2F332B8] | 00007FF7B2F332B8:"GameClientInterfaceV012" 00007FF7B1D2CAFF | FFD0 | CALL RAX | 00007FF7B1D2CB01 | 48:8947 10 | MOV QWORD PTR DS:[RDI+10],RAX |<--
The first 3 DWORDs I've marked in yellow are some constants I saw EAC writing in the game client structure. I thought maybe these are checked before an achievement is unlocked. The last highlight is your "timestamp" computed using RDTSC. Let's say your HWID. I thought this is also checked.
However, none of these led anywhere.
Then I looked at what's in [RDI+10], where the game client pointer is written to. And saw this:
So what I naturally did was to debug that 0x1 value and flip it between 1 and 0, while ending a mission. When the value is 0x0, you get no achievements. When it's 0x1, you do So the check is implemented in a very simple way to tell the game "hey, EAC was initialized successfully" -> write a 0x1.
Restarting the process and setting a breakpoint at [RDI+18] showed me where this 0x1 comes from:
So all I need to do with regards to future endeavors in the EAC emulation is to simply return 1 in the "IsEACInitialized" possibly-SDK function