-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.xml
793 lines (785 loc) · 167 KB
/
index.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>AVX's Blog</title>
<link>/</link>
<description>Recent content on AVX's Blog</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Mon, 17 Jul 2023 00:00:00 +0000</lastBuildDate><atom:link href="/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Dethroning the Jungle King: EasyAntiCheat's Import Protection</title>
<link>/posts/easyanticheat-import-protection/</link>
<pubDate>Mon, 17 Jul 2023 00:00:00 +0000</pubDate>
<guid>/posts/easyanticheat-import-protection/</guid>
<description>Disclaimer The information provided in this document is intended solely for educational and informational purposes. It is not meant to belittle EasyAntiCheat or any individuals involved in its development or implementation. Rather, it aims to shed light on the internal workings of EasyAntiCheat so that consumers can better understand what happens behind the scenes when playing their favorite games. Any opinions expressed herein do not necessarily reflect those of EasyAntiCheat or any other parties mentioned.</description>
<content><h1 id="disclaimer">Disclaimer</h1>
<p>The information provided in this document is intended <strong>solely for educational and informational purposes</strong>. It is <strong>not meant to belittle EasyAntiCheat</strong> or any individuals involved in its development or implementation. Rather, it aims to shed light on the internal workings of EasyAntiCheat so that consumers can better understand what happens behind the scenes when playing their favorite games. Any opinions expressed herein <strong>do not necessarily reflect those of EasyAntiCheat</strong> or any other parties mentioned. This document is provided &ldquo;<strong>as is</strong>&rdquo; without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and fitness for a particular purpose. I shall not be liable for any damages whatsoever arising out of or in connection with the use of this document.</p>
<h1 id="introduction">Introduction</h1>
<p>In the ever-evolving virtual battlegrounds, where the quest for fair play clashes with cunning cheaters, an enigmatic sentinel, EasyAntiCheat, reigns supreme, concealing its guarded export resolving strategies. This gripping journey takes us deep into the heart of its jungle-like defenses, as we endeavor to dethrone the Jungle King, exposing the hidden mechanisms, unlocking the secrets of their superior technology.</p>
<p>Armed with inquisitiveness and technical prowess, we embark on an enthralling expedition to shed light on the protected imports, unraveling the captivating tale of EasyAntiCheat&rsquo;s silent prowess in safeguarding the integrity of online gaming.</p>
<h1 id="before-we-dive-in-getting-the-basics-right">Before We Dive In: Getting the Basics Right</h1>
<p>To proceed with the remaining part of the article, I recommend first addressing these topics:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=DRH0oRFwFiM">Virtualization-Based Obfuscators</a></li>
<li><a href="http://sandsprite.com/CodeStuff/Understanding_imports.html">Understanding the Import Address Table</a></li>
<li><a href="https://dev.to/wireless90/exploring-the-export-table-windows-pe-internals-4l47">Exploring the Export Table</a></li>
</ul>
<h1 id="the-problem">The Problem</h1>
<p>Dynamic analysis plays a crucial role in understanding the behavior of code, especially when dealing with reverse engineering. Anticheats, such as EasyAntiCheat, have recognized the importance of this approach and actively develop methods to mitigate against such attacks.</p>
<p>One of the primary methods for understanding behavior is by observing calls to imported functions. Typically, these functions can be found in the application&rsquo;s IAT, which is resolved during the application&rsquo;s construction phase.</p>
<p>However, relying on the IAT for security products is considered quite an insecure design. To address this issue, one can reproduce the behavior by manually parsing the EAT and resolving imports themselves, which eliminates the need for using the IAT.</p>
<p>In the case of EasyAntiCheat, they have implemented this solution by employing a safeguarded virtualized routine, which attempts to securely resolve their needed exports, and also inlining decryption for the callers of the <code>DecryptImport</code> function.</p>
<p>Furthermore, on top of the inlined decryption, they are also employing encryption that utilizes a public and private key, with the private key being discarded at runtime.</p>
<p>With the use of this method, it becomes nearly impossible to recover the aforementioned private key or to overwrite the import with another value.</p>
<h1 id="spying-on-the-king-real-world-examples">Spying on the King: Real World Examples</h1>
<p>To kick things off, and before approaching the Jungle, let&rsquo;s examine some examples which are used in other applications.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span><span style="color:#f92672">*</span> VgkExports<span style="color:#f92672">::</span>ExEnumHandleTable(<span style="color:#66d9ef">void</span><span style="color:#f92672">*</span> a1)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> v17.m128i_i64[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0x3DC8C9558A64BA8A</span>i64;
</span></span><span style="display:flex;"><span> v18.m128i_i64[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0xD6EBDD7A0CEE792Bu</span>i64;
</span></span><span style="display:flex;"><span> v19.m128i_i64[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0x3DC8C9558A64BA8A</span>i64;
</span></span><span style="display:flex;"><span> v18.m128i_i64[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0xD6C6BCCF590828A8u</span>i64;
</span></span><span style="display:flex;"><span> v1 <span style="color:#f92672">=</span> _mm_load_si128(<span style="color:#f92672">&amp;</span>v18);
</span></span><span style="display:flex;"><span> v19.m128i_i64[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0xC7884760EDC680EDu</span>i64;
</span></span><span style="display:flex;"><span> v16.m128i_i64[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0xB7A3B00F62AB016Eu</span>i64;
</span></span><span style="display:flex;"><span> v16.m128i_i64[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0xBAA4DD9B3C644CC6u</span>i64;
</span></span><span style="display:flex;"><span> v17.m128i_i64[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0xC7884760EDC68088u</span>i64;
</span></span><span style="display:flex;"><span> v2 <span style="color:#f92672">=</span> <span style="color:#f92672">*</span>(<span style="color:#66d9ef">__int64</span> <span style="color:#f92672">**</span>)a1;
</span></span><span style="display:flex;"><span> v17 <span style="color:#f92672">=</span> _mm_xor_si128(v17, v19);
</span></span><span style="display:flex;"><span> v16 <span style="color:#f92672">=</span> _mm_xor_si128(v1, v16);
</span></span><span style="display:flex;"><span> Export <span style="color:#f92672">=</span> FindExport(<span style="color:#f92672">*</span>v2, v16.m128i_i8, <span style="color:#ae81ff">0</span>i64, <span style="color:#ae81ff">0</span>i64);
</span></span><span style="display:flex;"><span> <span style="color:#75715e">/* redacted code */</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> Export;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If you&rsquo;ve ever worked with VGK before, then you&rsquo;ll immediately notice these stubs spread throughout their binary.</p>
<p>The process is relatively straightforward:</p>
<ul>
<li>They start by constructing the string on the stack, applying XOR to obfuscate it, and calling <code>FindExport</code>.</li>
<li><code>FindExport</code> then takes the provided base address as the first argument and locates the EAT from that address.</li>
<li>Lastly, <code>FindExport</code> iterates through the EAT and compares the provided name with the names of exports to find a match.</li>
</ul>
<p>If you need further clarification on this technique, then I recommend exploring Justas Masiulis&rsquo; <a href="https://github.com/JustasMasiulis/lazy_importer">Lazy Importer</a>, which expands on the concept by employing a hash instead of a decryptable string, which improves the level of obfuscation and security.</p>
<h1 id="approaching-the-jungle-the-inlined-decryption">Approaching the Jungle: The Inlined Decryption</h1>
<p>As we know, EasyAntiCheat takes great pride in protecting against reverse engineering attempts, as can be seen from their new obfuscator.</p>
<p>So, instead of applying just one layer of encryption to the import addresses, they have opted to add another layer, known as their inlined decryption.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>v16 <span style="color:#f92672">=</span> DecryptImport(qword_125B00);
</span></span><span style="display:flex;"><span>((<span style="color:#66d9ef">void</span> (<span style="color:#66d9ef">__fastcall</span> <span style="color:#f92672">*</span>)(<span style="color:#66d9ef">__int64</span>))(__ROL8__(v16, <span style="color:#ae81ff">29</span>) <span style="color:#f92672">^</span> <span style="color:#ae81ff">0x3A505A07B9BA3B9E</span>i64))(v15);
</span></span></code></pre></div><p>Just as the name suggests, each caller to the <code>DecryptImport</code> function decrypts the second layer.</p>
<p>This is quite a play on their part as it makes it rather difficult to hook imports without knowing the decryption algorithm.</p>
<p>Of course, one can simply just employ an assembly disassembler and automatically resolve the constants used for decryption.</p>
<p>While this method certainly performs well in most scenarios, it proves to be a great challenge in functions that leverage obfuscation.</p>
<p>Another feasible approach would be to capture the context after returning to the obfuscated code from <code>DecryptImport</code> and emulate until the decryption has completed.</p>
<p>However, where&rsquo;s the fun in doing that?</p>
<h1 id="approaching-the-jungle-the-main-decryption">Approaching the Jungle: The Main Decryption</h1>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">uint64_t</span> <span style="color:#a6e22e">DecryptImport</span>( <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">*</span> PublicKeys )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span> First <span style="color:#f92672">=</span> DecryptFirst( PublicKeys[ <span style="color:#ae81ff">0</span> ] );
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span> Second <span style="color:#f92672">=</span> DecryptSecond( PublicKeys[ <span style="color:#ae81ff">1</span> ] ) <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">32</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> First <span style="color:#f92672">|</span> Second;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Based on both of these reversed snippets, I can infer what the encryption process may look like:</p>
<ul>
<li>Iterate and find the export in a driver&rsquo;s EAT.</li>
<li>Apply the inlined encryption to the result.</li>
<li>Apply the main encryption to the result.</li>
<li>Write both public keys to the designated region.</li>
</ul>
<h1 id="entering-the-jungle-setting-the-traps">Entering the Jungle: Setting the Traps</h1>
<p>With that out of the way, I started my analysis by setting a write trap on the public keys of a random encrypted import in the <code>.data</code> section.</p>
<p>This enabled me to see what I was dealing with and continue reversing from there.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>AddEptHook_Range( EacBase <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x125B00</span>, EacBase <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x125B08</span>, HvDebugger<span style="color:#f92672">::</span>HvAccess<span style="color:#f92672">::</span>Write,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">+</span>[ ]( HvDebugger<span style="color:#f92672">::</span>HvContext<span style="color:#f92672">*</span> Context, <span style="color:#66d9ef">uint64_t</span> Address, <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">&amp;</span> Pfn )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> WriteLog( <span style="color:#e6db74">&#34;WriteTrap&#34;</span>, TraceLoggingHexUInt64( EAC_BASE( Context<span style="color:#f92672">-&gt;</span>Rip ), <span style="color:#e6db74">&#34;RipRva&#34;</span> ), TraceLoggingHexUInt64( EAC_BASE( Address ), <span style="color:#e6db74">&#34;AddrRva&#34;</span> ) );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> false;
</span></span><span style="display:flex;"><span>} );
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;RipRva&#34;</span>:<span style="color:#e6db74">&#34;0x91FB7E&#34;</span>, <span style="color:#f92672">&#34;AddrRva&#34;</span>:<span style="color:#e6db74">&#34;0x125B00&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;RipRva&#34;</span>:<span style="color:#e6db74">&#34;0xD1C721&#34;</span>, <span style="color:#f92672">&#34;AddrRva&#34;</span>:<span style="color:#e6db74">&#34;0x125B08&#34;</span> }
</span></span></code></pre></div><p>There were two other writes, but these are used (and read) before the export is found, so I didn&rsquo;t mention them here.</p>
<p>After looking at these addresses, it&rsquo;s pretty obvious that it&rsquo;s coming from a virtualized function, which was my initial anticipation.</p>
<h1 id="entering-the-jungle-following-the-river">Entering the Jungle: Following the River</h1>
<p>Since I was now analyzing a virtualized routine, specifically a <code>VMWRITE</code> handler, I figured that I&rsquo;d take a look at the stack as interesting stuff that could be useful to our investigation may be present on it.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">*</span> Stack <span style="color:#f92672">=</span> ( <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">*</span> )Context<span style="color:#f92672">-&gt;</span>Rsp;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> ( size_t Idx <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; Idx <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">200</span>; Idx<span style="color:#f92672">++</span> )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span> Value <span style="color:#f92672">=</span> Stack[ Idx ];
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( Value <span style="color:#f92672">&amp;&amp;</span> IN_EAC( Value ) )
</span></span><span style="display:flex;"><span> WriteLog( <span style="color:#e6db74">&#34;StackDump&#34;</span>, TraceLoggingHexUInt64( Idx ), TraceLoggingHexUInt64( EAC_BASE( Value ), <span style="color:#e6db74">&#34;Rva&#34;</span> ) );
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x0&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x125B00&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Trap</span> <span style="color:#960050;background-color:#1e0010">Location</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x23&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x6F4E4&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x24&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x6F4E4&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x25&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x6F598&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x4B&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x66698C&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x4D&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x125B00&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Trap</span> <span style="color:#960050;background-color:#1e0010">Location</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x52&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x18F9E4&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">DriverEntry</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x63&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x18FAC0&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x65&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x18FA80&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x6D&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x74F8E2&#34;</span> }
</span></span></code></pre></div><p>After quickly looking at the results, I noticed that <code>0x18F9E4</code> was the <code>DriverEntry</code> function, and so everything after that could be easily disregarded.</p>
<p>Another thing that can be disregarded is <code>0x125B00</code>, which is the address that I set the trap to in the first place.</p>
<p>After removing duplicates, I was left with the following results:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x23&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x6F4E4&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x25&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x6F598&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x4B&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x66698C&#34;</span> }
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">__int64</span> <span style="color:#66d9ef">__fastcall</span> <span style="color:#a6e22e">sub_6F4E4</span>(<span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">__int64</span> a1, <span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">__int64</span> a2, <span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">int</span> a3)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">__int64</span> v3; <span style="color:#75715e">// r9
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">__int64</span> i; <span style="color:#75715e">// r8
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span> v3 <span style="color:#f92672">=</span> a3;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> ( i <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>i64; a1; v3 <span style="color:#f92672">=</span> v3 <span style="color:#f92672">*</span> (<span style="color:#66d9ef">unsigned</span> __int128)v3 <span style="color:#f92672">%</span> a2 )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( (a1 <span style="color:#f92672">&amp;</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span> )
</span></span><span style="display:flex;"><span> i <span style="color:#f92672">=</span> (<span style="color:#66d9ef">unsigned</span> <span style="color:#66d9ef">__int64</span>)i <span style="color:#f92672">*</span> (<span style="color:#66d9ef">unsigned</span> __int128)v3 <span style="color:#f92672">%</span> a2;
</span></span><span style="display:flex;"><span> a1 <span style="color:#f92672">&gt;&gt;=</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> i;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Just from looking at the general structure of this function, it reminded me of the initial <code>DecryptImport</code> routine which we went over earlier.</p>
<p>However, it was quite odd that they&rsquo;d be decrypting the imports before they were even written.</p>
<p>So, judging by the arguments, and after setting a breakpoint and dumping the registers, it became obvious that this routine is actually used to apply the main encryption to each respective import.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rcx&#34;</span>:<span style="color:#e6db74">&#34;0x35375306D545459&#34;</span>, <span style="color:#f92672">&#34;Rdx&#34;</span>:<span style="color:#e6db74">&#34;0x12D8ED6858CD15B7&#34;</span>, <span style="color:#f92672">&#34;R8&#34;</span>:<span style="color:#e6db74">&#34;0xA588E17A&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rcx&#34;</span>:<span style="color:#e6db74">&#34;0x1D34200DE5B033A1&#34;</span>, <span style="color:#f92672">&#34;Rdx&#34;</span>:<span style="color:#e6db74">&#34;0x237626ED2C9C28F3&#34;</span>, <span style="color:#f92672">&#34;R8&#34;</span>:<span style="color:#e6db74">&#34;0x20FAECB8&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rcx&#34;</span>:<span style="color:#e6db74">&#34;0x35375306D545459&#34;</span>, <span style="color:#f92672">&#34;Rdx&#34;</span>:<span style="color:#e6db74">&#34;0x12D8ED6858CD15B7&#34;</span>, <span style="color:#f92672">&#34;R8&#34;</span>:<span style="color:#e6db74">&#34;0x63558ACF&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rcx&#34;</span>:<span style="color:#e6db74">&#34;0x1D34200DE5B033A1&#34;</span>, <span style="color:#f92672">&#34;Rdx&#34;</span>:<span style="color:#e6db74">&#34;0x237626ED2C9C28F3&#34;</span>, <span style="color:#f92672">&#34;R8&#34;</span>:<span style="color:#e6db74">&#34;0xAD8C5A8&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rcx&#34;</span>:<span style="color:#e6db74">&#34;0x35375306D545459&#34;</span>, <span style="color:#f92672">&#34;Rdx&#34;</span>:<span style="color:#e6db74">&#34;0x12D8ED6858CD15B7&#34;</span>, <span style="color:#f92672">&#34;R8&#34;</span>:<span style="color:#e6db74">&#34;0x30696C03&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rcx&#34;</span>:<span style="color:#e6db74">&#34;0x1D34200DE5B033A1&#34;</span>, <span style="color:#f92672">&#34;Rdx&#34;</span>:<span style="color:#e6db74">&#34;0x237626ED2C9C28F3&#34;</span>, <span style="color:#f92672">&#34;R8&#34;</span>:<span style="color:#e6db74">&#34;0x46D4F31E&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rcx&#34;</span>:<span style="color:#e6db74">&#34;0x35375306D545459&#34;</span>, <span style="color:#f92672">&#34;Rdx&#34;</span>:<span style="color:#e6db74">&#34;0x12D8ED6858CD15B7&#34;</span>, <span style="color:#f92672">&#34;R8&#34;</span>:<span style="color:#e6db74">&#34;0xCFBFE39F&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rcx&#34;</span>:<span style="color:#e6db74">&#34;0x1D34200DE5B033A1&#34;</span>, <span style="color:#f92672">&#34;Rdx&#34;</span>:<span style="color:#e6db74">&#34;0x237626ED2C9C28F3&#34;</span>, <span style="color:#f92672">&#34;R8&#34;</span>:<span style="color:#e6db74">&#34;0xBB301A01&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rcx&#34;</span>:<span style="color:#e6db74">&#34;0x35375306D545459&#34;</span>, <span style="color:#f92672">&#34;Rdx&#34;</span>:<span style="color:#e6db74">&#34;0x12D8ED6858CD15B7&#34;</span>, <span style="color:#f92672">&#34;R8&#34;</span>:<span style="color:#e6db74">&#34;0xD4D49738&#34;</span> }
</span></span></code></pre></div><p>Yet again, this reminded me of the initial <code>DecryptImport</code> function, as <code>RDX</code>, which represents the public key, was exactly the same.</p>
<p>This function is being called twice, with each public and private key, on the inline encrypted values, which is represented using <code>R8</code>.</p>
<p>The next function on the list, namely <code>0x6F598</code>, had also piqued my interest during the investigation.</p>
<p>Much like the previous function, I also dumped the registers and noticed something rather interesting about the registers at hand.</p>
<p>When I looked at the registers, <code>RCX</code> was the only interesting one, as it pointed to <code>0x1861D0</code>, which was an empty region in the <code>.data</code> section.</p>
<p>I decided to continue searching for anything else that was interesting and kept this address in mind.</p>
<p>Lastly, I was left with a <code>VMCALL</code> handler, which after some quick analysis, didn&rsquo;t turn out useful.</p>
<h1 id="entering-the-jungle-the-kings-den">Entering the Jungle: The King&rsquo;s Den</h1>
<p>To gather some more information and determine how they were comparing the names, I decided to set a read trap on <code>NtGlobalFlag</code>&rsquo;s name in <code>ntoskrnl.exe</code>&rsquo;s EAT, as I already knew that they&rsquo;re using this import.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>AddEptHook( Utils<span style="color:#f92672">::</span>GetExportName( <span style="color:#e6db74">&#34;NtGlobalFlag&#34;</span> ), HvDebugger<span style="color:#f92672">::</span>HvAccess<span style="color:#f92672">::</span>Read,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">+</span>[ ]( HvDebugger<span style="color:#f92672">::</span>HvContext<span style="color:#f92672">*</span> Context, <span style="color:#66d9ef">uint64_t</span> Address, <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">&amp;</span> Pfn )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> WriteLog( <span style="color:#e6db74">&#34;ReadTrap&#34;</span>, TraceLoggingHexUInt64( EAC_BASE( Context<span style="color:#f92672">-&gt;</span>Rip ), <span style="color:#e6db74">&#34;Rva&#34;</span> ) );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">*</span> Stack <span style="color:#f92672">=</span> ( <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">*</span> )Context<span style="color:#f92672">-&gt;</span>Rsp;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> ( size_t Idx <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; Idx <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">200</span>; Idx<span style="color:#f92672">++</span> )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span> Value <span style="color:#f92672">=</span> Stack[ Idx ];
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( Value <span style="color:#f92672">&amp;&amp;</span> IN_EAC( Value ) )
</span></span><span style="display:flex;"><span> WriteLog( <span style="color:#e6db74">&#34;StackDump&#34;</span>, TraceLoggingHexUInt64( Idx ), TraceLoggingHexUInt64( EAC_BASE( Value ), <span style="color:#e6db74">&#34;Rva&#34;</span> ) );
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> false;
</span></span><span style="display:flex;"><span>} );
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x2FE234&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Read</span> <span style="color:#960050;background-color:#1e0010">Handler</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x4637B&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">SHA</span><span style="color:#ae81ff">1</span> <span style="color:#960050;background-color:#1e0010">Read</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0xF&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x6B09A5&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">SHA</span><span style="color:#ae81ff">1</span> <span style="color:#960050;background-color:#1e0010">Caller</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x13&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x463AC&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">SHA</span><span style="color:#ae81ff">1</span> <span style="color:#960050;background-color:#1e0010">Function</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x1E&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x125600&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Export</span> <span style="color:#960050;background-color:#1e0010">Keys</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x1F&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x6F584&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Placeholder</span> <span style="color:#960050;background-color:#1e0010">Function</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x3A&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x1861D0&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Chunk</span> <span style="color:#960050;background-color:#1e0010">in</span> <span style="color:#960050;background-color:#1e0010">.data</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x3D&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x924C56&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x67&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x66698C&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Useless</span> <span style="color:#960050;background-color:#1e0010">Function</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x69&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x1260F8&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Random</span> <span style="color:#960050;background-color:#1e0010">Export</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x6E&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x18F9E4&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">DriverEntry</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x7F&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x18FAC0&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x81&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x18FA80&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Idx&#34;</span>:<span style="color:#e6db74">&#34;0x89&#34;</span>, <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x74F8E2&#34;</span> }
</span></span></code></pre></div><p>That&rsquo;s exciting to see! They were still relying on their SHA1 algorithm to read and compare the names of exports, which I noticed from the distinct constants that are used in the SHA1 context.</p>
<p>Since the SHA1 routine was provided with the length, and the initial reading handlers were reading all of the bytes in the name, I came to the conclusion that it was an inlined <code>strlen</code>, which was virtualized.</p>
<p>Just like before, I removed the useless entries that are labeled, which left me with a single entry: <code>0x924C56</code>.</p>
<p>It&rsquo;s also important to note that, yet again, <code>0x1861D0</code> appears, so I deemed it quite important.</p>
<p>Now, given that the entry was a <code>VMCALL</code> instruction, I dumped the destination and registers alike.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>AddEptHook( EacBase <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x924C52</span>, HvDebugger<span style="color:#f92672">::</span>HvAccess<span style="color:#f92672">::</span>Execute,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">+</span>[ ]( HvDebugger<span style="color:#f92672">::</span>HvContext<span style="color:#f92672">*</span> Context, <span style="color:#66d9ef">uint64_t</span> Address, <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">&amp;</span> Pfn )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> seg007:0000000000924C4B add rsp, 110h
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> seg007:0000000000924C52 call qword ptr [rsp+8]
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> seg007:0000000000924C56 sub rsp, 110h
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> */</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span> Function <span style="color:#f92672">=</span> <span style="color:#f92672">*</span>( <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">*</span> )( Context<span style="color:#f92672">-&gt;</span>Rsp <span style="color:#f92672">+</span> <span style="color:#ae81ff">8</span> );
</span></span><span style="display:flex;"><span> WriteLog( <span style="color:#e6db74">&#34;Vmcall&#34;</span>, TraceLoggingHexUInt64( EAC_BASE( Function ), <span style="color:#e6db74">&#34;Function&#34;</span> ) );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> HvDebugger<span style="color:#f92672">::</span>DumpRegisters( Context );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> false;
</span></span><span style="display:flex;"><span>} );
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Function&#34;</span>:<span style="color:#e6db74">&#34;0x72E98&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;RCX&#34;</span>, <span style="color:#f92672">&#34;Value&#34;</span>:<span style="color:#e6db74">&#34;0xFFFF8E8BAE7255E0&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;RDX&#34;</span>, <span style="color:#f92672">&#34;Value&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF80706E00000&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Ntoskrnl</span> <span style="color:#960050;background-color:#1e0010">Base</span> <span style="color:#960050;background-color:#1e0010">Address</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;R8&#34;</span>, <span style="color:#f92672">&#34;Value&#34;</span>:<span style="color:#e6db74">&#34;0xFFFF8E8BAE7256C0&#34;</span> }
</span></span></code></pre></div><p>Awesome, this was exactly the function that I was looking for, which I deemed as <code>InitializeImports</code>!</p>
<p>This function, in fact, was called for every module that EasyAntiCheat wanted to initialize protected imports for, and was provided with the base address.</p>
<p>After further analysis, the function&rsquo;s parameters are as follows:</p>
<ul>
<li><code>RCX</code>: A pointer to both public keys.</li>
<li><code>RDX</code>: The module&rsquo;s base address.</li>
<li><code>R8</code>: A pointer to an integer that was written the number of resolved imports, which was determinated by logging writes, and observing the integer increasing when an import was resolved.</li>
</ul>
<p>While this is definitely all useful information, I still had the question: how did they know which imports to find?</p>
<p>As I mentioned previously, none of the registers contained anything that held this information.</p>
<p>But then I thought of something: what if they were writing it to <code>0x1861D0</code>?</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>AddEptHook( EacBase <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x1861D0</span>, HvDebugger<span style="color:#f92672">::</span>HvAccess<span style="color:#f92672">::</span>Read,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">+</span>[ ]( HvDebugger<span style="color:#f92672">::</span>HvContext<span style="color:#f92672">*</span> Context, <span style="color:#66d9ef">uint64_t</span> Address, <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">&amp;</span> Pfn )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> WriteLog( <span style="color:#e6db74">&#34;ReadTrap&#34;</span>, TraceLoggingString( Symbolize( Context<span style="color:#f92672">-&gt;</span>Rip ), <span style="color:#e6db74">&#34;Name&#34;</span> ) );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> false;
</span></span><span style="display:flex;"><span>} );
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;EasyAntiCheat_EOS.sys+0x73228&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Sorting</span> <span style="color:#960050;background-color:#1e0010">Function</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;ntoskrnl.exe+0x3D0A9A&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">qsort</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;ntoskrnl.exe+0x3D0AD3&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">qsort</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;EasyAntiCheat_EOS.sys+0x7322B&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Sorting</span> <span style="color:#960050;background-color:#1e0010">Function</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;ntoskrnl.exe+0x3D0A23&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">qsort</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;EasyAntiCheat_EOS.sys+0x21F118&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Reading</span> <span style="color:#960050;background-color:#1e0010">Handler</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;EasyAntiCheat_EOS.sys+0x855527&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Reading</span> <span style="color:#960050;background-color:#1e0010">Handler</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;EasyAntiCheat_EOS.sys+0x425042&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Reading</span> <span style="color:#960050;background-color:#1e0010">Handler</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;EasyAntiCheat_EOS.sys+0x86D2BC&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Reading</span> <span style="color:#960050;background-color:#1e0010">Handler</span>
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Name&#34;</span>:<span style="color:#e6db74">&#34;EasyAntiCheat_EOS.sys+0x425042&#34;</span> } <span style="color:#960050;background-color:#1e0010">&lt;--</span> <span style="color:#960050;background-color:#1e0010">Reading</span> <span style="color:#960050;background-color:#1e0010">Handler</span>
</span></span></code></pre></div><p>This was quite interesting, as sorting them is definitely smart on their end, as it allows for faster iteration later on.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>AddEptHook( Utils<span style="color:#f92672">::</span>GetExport( <span style="color:#e6db74">&#34;qsort&#34;</span> ), HvDebugger<span style="color:#f92672">::</span>HvAccess<span style="color:#f92672">::</span>Execute,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">+</span>[ ]( HvDebugger<span style="color:#f92672">::</span>HvContext<span style="color:#f92672">*</span> Context, uintptr_t Address, <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">&amp;</span> Pfn )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span> ReturnAddress <span style="color:#f92672">=</span> <span style="color:#f92672">*</span>( <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">*</span> )Context<span style="color:#f92672">-&gt;</span>Rsp;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( <span style="color:#f92672">!</span>IN_EAC( ReturnAddress ) )
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> false;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> Utils<span style="color:#f92672">::</span>DumpArray( <span style="color:#f92672">&amp;</span>FormatElements, Context<span style="color:#f92672">-&gt;</span>Gpr<span style="color:#f92672">-&gt;</span>rcx <span style="color:#75715e">/* Base of Elements */</span>, Context<span style="color:#f92672">-&gt;</span>Gpr<span style="color:#f92672">-&gt;</span>rdx <span style="color:#75715e">/* Number of Elements */</span>, Context<span style="color:#f92672">-&gt;</span>Gpr<span style="color:#f92672">-&gt;</span>r8 <span style="color:#75715e">/* Size of Elements */</span> );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> false;
</span></span><span style="display:flex;"><span>} );
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Sort&#34;</span>:<span style="color:#e6db74">&#34;0x6556BC1D6053223C&#34;</span>, <span style="color:#f92672">&#34;InlinedKey&#34;</span>:<span style="color:#e6db74">&#34;0x65091738C0592277&#34;</span>, <span style="color:#f92672">&#34;Export&#34;</span>:<span style="color:#e6db74">&#34;0x1263E8&#34;</span>, <span style="color:#f92672">&#34;Default&#34;</span>:<span style="color:#e6db74">&#34;0x6F584&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Sort&#34;</span>:<span style="color:#e6db74">&#34;0x386399B9B0FD723E&#34;</span>, <span style="color:#f92672">&#34;InlinedKey&#34;</span>:<span style="color:#e6db74">&#34;0xF26FB57ABCF6FADF&#34;</span>, <span style="color:#f92672">&#34;Export&#34;</span>:<span style="color:#e6db74">&#34;0x126408&#34;</span>, <span style="color:#f92672">&#34;Default&#34;</span>:<span style="color:#e6db74">&#34;0x6F584&#34;</span> }
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">ProtectedImportData</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span> Sort;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span> InlinedKey;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span><span style="color:#f92672">*</span> PublicKeys;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">uint64_t</span> Default;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>This dump was much larger, but I condensed it for readability.</p>
</blockquote>
<p>My suspicions were correct, as EasyAntiCheat is indeed temporarily storing the import data here, and wiping it after they&rsquo;ve encrypted their imports.</p>
<p>Another interesting thing is that, if the import couldn&rsquo;t be resolved, they resort to using default values instead, which I noticed after looking at the default values in IDA.</p>
<p>On a final note, EasyAntiCheat also uses <code>RtlPcToFileHeader</code>, on itself, to locate the base address for <code>ntoskrnl.exe</code>. The base address is then encrypted, and stored, within the <code>.data</code> section, with a boolean to say that it was found.</p>
<h1 id="leaving-the-jungle-winning-the-battle">Leaving the Jungle: Winning the Battle</h1>
<p>In conclusion, EasyAntiCheat has opted for quite an effective method in dealing with hardening against reverse engineering attempts.</p>
<p>However, like with anything in this field, it is subject to being analyzed.</p>
<p>As per usual, I have left an exercise for the reader, which you will quickly notice when attempting to generate decryption stubs for the imports.</p>
<p>Until next time!</p>
</content>
</item>
<item>
<title>The Unseen Guardian: EasyAntiCheat’s EProcess Emulation</title>
<link>/posts/easyanticheat-eprocess-emulation/</link>
<pubDate>Thu, 06 Jul 2023 00:00:00 +0000</pubDate>
<guid>/posts/easyanticheat-eprocess-emulation/</guid>
<description>Disclaimer The information provided in this document is intended solely for educational and informational purposes. It is not meant to belittle EasyAntiCheat or any individuals involved in its development or implementation. Rather, it aims to shed light on the internal workings of EasyAntiCheat so that consumers can better understand what happens behind the scenes when playing their favorite games. Any opinions expressed herein do not necessarily reflect those of EasyAntiCheat or any other parties mentioned.</description>
<content><h1 id="disclaimer">Disclaimer</h1>
<p>The information provided in this document is intended <strong>solely for educational and informational purposes</strong>. It is <strong>not meant to belittle EasyAntiCheat</strong> or any individuals involved in its development or implementation. Rather, it aims to shed light on the internal workings of EasyAntiCheat so that consumers can better understand what happens behind the scenes when playing their favorite games. Any opinions expressed herein <strong>do not necessarily reflect those of EasyAntiCheat</strong> or any other parties mentioned. This document is provided &ldquo;<strong>as is</strong>&rdquo; without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and fitness for a particular purpose. I shall not be liable for any damages whatsoever arising out of or in connection with the use of this document.</p>
<h1 id="introduction">Introduction</h1>
<p>In a world where virtual battlegrounds have become the arena for fierce competition, cheaters threaten to undermine the very foundations of fair play. But what if the key to safeguarding the integrity of online gaming lies in an elusive strategy, cleverly concealed from prying eyes? Enter EasyAntiCheat, the silent sentinel dedicated to preserving the sanctity of gaming realms.</p>
<p>As we dive into the depths of this captivating tale, we uncover a hidden gem that unfolds like a thrilling detective novel. Beneath the surface of EasyAntiCheat&rsquo;s armor, a remarkable methodology emerges—one that involves the cunning emulation of <code>NtCreateUserProcess</code>, subtly controlling the very essence of construction within the gaming universe.</p>
<h1 id="before-we-dive-in-getting-the-basics-right">Before We Dive In: Getting the Basics Right</h1>
<p>To proceed with the remaining part of the article, I recommend first addressing these topics:</p>
<ul>
<li><a href="https://advancedvectorextensions.github.io/posts/easyanticheat-cr3-protection/">EasyAntiCheat&rsquo;s CR3 Protection</a></li>
<li><a href="https://www.youtube.com/watch?v=DRH0oRFwFiM">Virtualization-Based Obfuscators</a></li>
<li><a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/eprocess">Windows Kernel Opaque Structures</a></li>
</ul>
<h1 id="the-problem">The Problem</h1>
<p>In the past, it was possible to register a process creation callback and obtain the game&rsquo;s real <code>CR3</code> before EasyAntiCheat modified it. However, little did we know that a significant change was about to take place.</p>
<p>As I was going about my daily routine, a message from my friend popped up on Discord, stating that his cheat was no longer functional. The reason behind this sudden disruption? Registering a process creation callback now yielded an incorrect <code>CR3</code>.</p>
<p>Naturally, I couldn&rsquo;t let this revelation pass without verifying it firsthand. Intrigued and determined, I embarked on a mission to uncover the truth.</p>
<p><img src="https://i.imgur.com/db1KvTC.png" alt=""></p>
<p>Before diving deep, and to get a rough idea of what they were doing, I installed a hook on <code>PspInsertProcess</code> to see if the <code>CR3</code> had been altered. To my surprise, it had been!</p>
<p>This was quite odd because there were no easy hook points in <code>PspAllocateProcess</code> without installing a hook that was local to the game&rsquo;s launcher process, which required doing trickery with the <code>PFN</code>, and is rather a versatile approach.</p>
<p>But, more importantly, what does this change mean? Well, it simply means that either scanning for the <code>CR3</code> or decrypting it is now required.</p>
<h1 id="the-interrogation-who-dares-touch-the-eprocess">The Interrogation: Who Dares Touch the EProcess?</h1>
<p>To initiate my reversing process, I needed to obtain access to the <code>EProcess</code> allocation before EasyAntiCheat made any modifications to it.</p>
<p>During my investigation of the <code>PspAllocateProcess</code> function in search of a convenient hook point, I stumbled upon the <code>PspInitializeProcessLock</code> function, which proved to be relatively simple to re-implement.</p>
<p><img src="https://i.imgur.com/hYcJUxw.png" alt=""></p>
<blockquote>
<p>While I could&rsquo;ve hooked another function, this function didn&rsquo;t require using a trampoline.</p>
</blockquote>
<p>If we delve deeper into the allocation function, we encounter a function named <code>MmCreateProcessAddressSpace</code>, which is responsible for writing to the process&rsquo; <code>CR3</code>.</p>
<p><img src="https://i.imgur.com/RMy5hRd.png" alt=""></p>
<p>So, by understanding where the <code>CR3</code> is written, we can determine if it is intercepted by EasyAntiCheat, and not written by the original location.</p>
<p>To accomplish this, I decided to utilize my hypervisor and set an <code>EPT</code> breakpoint on the page that contained the <code>EProcess</code>.</p>
<p>Considering that the function was likely to be called from within the launcher&rsquo;s context, I simply compared the name to the launcher&rsquo;s name for verification.</p>
<p><img src="https://i.imgur.com/63FIn9n.png" alt=""></p>
<p>Since the <code>EProcess</code> might not be allocated on a page boundary, I saved the address and checked if the violation occurred within the specified range.</p>
<p><img src="https://i.imgur.com/KXGFD5c.png" alt=""></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x58233A&#34;</span>, <span style="color:#f92672">&#34;Rbp&#34;</span>: <span style="color:#e6db74">&#34;0x4000000853FFF000&#34;</span>, <span style="color:#960050;background-color:#1e0010">...Other</span> <span style="color:#960050;background-color:#1e0010">Registers...</span> }
</span></span></code></pre></div><p>Now, as you can clearly observe, this is precisely where EasyAntiCheat is writing their manipulated <code>CR3</code> value to the <code>EProcess</code>.</p>
<p><img src="https://i.imgur.com/Mjh781a.png" alt=""></p>
<p>Unfortunately, but not surprisingly, the routine responsible for writing the <code>CR3</code> is virtualized.</p>
<p>Instead of investing time and effort into tracing and lifting the VM, an alternative approach would be to search for other sections of code that are not virtualized.</p>
<p>However, it&rsquo;s crucial to remember and keep track of the mentioned piece of code, as it holds significance for future stages.</p>
<h1 id="the-interrogation-the-search-party">The Interrogation: The Search Party</h1>
<p>As we are aware from the previous section, EasyAntiCheat is somehow intercepting the <code>PspAllocateProcess</code> function and modifying the <code>CR3</code> very early on in the process.</p>
<p>Considering this information, it is not far-fetched to speculate that they may also be writing to the other components? To confirm this, I removed the offset check, and logged every write to the <code>EProcess</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x42106&#34;</span>, <span style="color:#f92672">&#34;Offset&#34;</span>:<span style="color:#e6db74">&#34;0x5E8&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x42106&#34;</span>, <span style="color:#f92672">&#34;Offset&#34;</span>:<span style="color:#e6db74">&#34;0x5E0&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x42106&#34;</span>, <span style="color:#f92672">&#34;Offset&#34;</span>:<span style="color:#e6db74">&#34;0x8A8&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x42106&#34;</span>, <span style="color:#f92672">&#34;Offset&#34;</span>:<span style="color:#e6db74">&#34;0x8A0&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x42106&#34;</span>, <span style="color:#f92672">&#34;Offset&#34;</span>:<span style="color:#e6db74">&#34;0x998&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;Rva&#34;</span>:<span style="color:#e6db74">&#34;0x42106&#34;</span>, <span style="color:#f92672">&#34;Offset&#34;</span>:<span style="color:#e6db74">&#34;0x990&#34;</span> }
</span></span></code></pre></div><blockquote>
<p>This log was quite a big bigger, but I stripped it down for clarity&rsquo;s sake.</p>
</blockquote>
<p>After following the Rva, I was introduced with a wonderful looking switch case.</p>
<p><img src="https://i.imgur.com/GmyyqmN.png" alt=""></p>
<p>Now, when examining this, what does it appear to be? Well, to me, it resembles some kind of function for writing to registers.</p>
<p>To further validate my suspicions, I logged the return address and proceeded to track its path.</p>
<p><img src="https://i.imgur.com/PSzWLFJ.png" alt=""></p>
<p><img src="https://i.imgur.com/o8kkNha.png" alt=""></p>
<p>Well, this turned out to be a highly successful endeavor, as upon further exploration of the function, it became apparent that it was in fact a CPU emulator!</p>
<p><img src="https://i.imgur.com/TZ6Hi7Z.png" alt=""></p>
<p><img src="https://i.imgur.com/GjZfcxD.png" alt=""></p>
<p>Indeed, now everything falls into place. It appears that they were emulating the <code>EProcess</code> construction process and manipulating the <code>CR3</code> write through their emulation engine. This explains how EasyAntiCheat was able to intercept and modify the <code>CR3</code> value at such an early stage.</p>
<h1 id="the-interrogation-the-operator">The Interrogation: The Operator</h1>
<p>Driven by my curiosity, I persisted in tracing the return stack as I was eager to uncover the exact workings of this emulation engine, and who dared to operate it.</p>
<p><img src="https://i.imgur.com/f77zXwi.png" alt=""></p>
<p><img src="https://i.imgur.com/STyhKlO.png" alt=""></p>
<p>I understand that the code may appear obfuscated, but rest assured, it holds valuable significance for our investigation.</p>
<p>As you may have already deduced, this is, in fact, a handler within their VM, which I have identified as <code>BRANCHCALL</code>.</p>
<p>Let&rsquo;s begin by tracing the branch that is executed when the emulation succeeds, which has the offset <code>0xFFFFFFFFF844BC37</code>.</p>
<p><img src="https://i.imgur.com/Uku8hDb.png" alt=""></p>
<blockquote>
<p>This is known as the dispatcher, which the handler uses to dispatch control flow.</p>
</blockquote>
<p>It seems relatively straightforward. By adding both constants together, we obtain another branch, which leads to an <code>EXIT</code> handler.</p>
<p><img src="https://i.imgur.com/O6tVajD.png" alt=""></p>
<p>In the other branch, which is executed when the emulation is not completed, the code continues the loop and proceeds with further emulation.</p>
<h1 id="the-interrogation-stealing-the-flow">The Interrogation: Stealing the Flow</h1>
<p>To determine the starting and ending points of the emulation process, I referred to my reliable hypervisor and hooked the <code>BRANCHCALL</code> handler.</p>
<p><img src="https://i.imgur.com/Rs2MYmc.png" alt=""></p>
<blockquote>
<p>These offsets were gathered by inspecting the assembly in the handler, and are relative to the VM.</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF80681274376&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF80681274379&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF8068127437E&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF80681274385&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF80681274387&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF80681274389&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF8068127438B&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF8068127438D&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF8068127438E&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF8068127438F&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF80681274390&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0xFFFFF80681274391&#34;</span> }
</span></span><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;vRip&#34;</span>:<span style="color:#e6db74">&#34;0x0000000000000000&#34;</span> }
</span></span></code></pre></div><blockquote>
<p>This log was quite a big bigger, but I stripped it down for clarity&rsquo;s sake.</p>
</blockquote>
<p>They were actually emulating even further back than I initially suspected. From analyzing my log, it became apparent that they were emulating <code>NtCreateUserProcess</code>!</p>
<p>To halt the emulation engine at the end of the function, they set the return address to <code>NULL</code>, effectively stopping the emulation process.</p>
<h1 id="the-interrogation-the-backbone">The Interrogation: The Backbone</h1>
<p>Remember those random function pointers in the emulator&rsquo;s context? Well, they&rsquo;re not so random afterall, and are actually the backbone of this entire process.</p>
<p>Let&rsquo;s start with analyzing this one, which lives at the top of the emulator&rsquo;s function.</p>
<p><img src="https://i.imgur.com/sLuAswx.png" alt=""></p>
<p><img src="https://i.imgur.com/kveRWUS.png" alt=""></p>
<p>Well, isn&rsquo;t that something! Now the puzzle is starting to come together, as that particular <code>QWORD</code> is actually the address of the instruction responsible for writing the <code>CR3</code> value, which we found in <code>MmCreateProcessAddressSpace</code>.</p>
<p>To add the final touch, that function is even the virtualized function that performed the write operation on the <code>CR3</code>, which we traced in the previous sections.</p>
<p>Even after all of this, the question still remains: are they truly emulating every single call within <code>NtCreateUserProcess</code>?</p>
<p>After looking at the rest of the emulator&rsquo;s code, I discovered the section that handles the execution or emulation of subroutines.</p>
<p><img src="https://i.imgur.com/0DabId3.png" alt=""></p>
<p><img src="https://i.imgur.com/3isRzQ3.png" alt=""></p>
<p>The callback itself is quite straightforward and simply emulates the code if the function leads to <code>MmCreateProcessAddressSpace</code>.</p>
<p><img src="https://i.imgur.com/C8tfwM9.png" alt=""></p>
<h1 id="conclusion">Conclusion</h1>
<p>In conclusion, EasyAntiCheat has opened up a Pandora&rsquo;s box that they cannot close. They have revealed their capabilities, and there is so much more they can unleash.</p>
<p>I eagerly am waiting to document any future developments that EasyAntiCheat introduces.</p>
<p>Until the next revelation!</p>
</content>
</item>
<item>
<title>Unleashing the Secrets: EasyAntiCheat’s CR3 Protection</title>
<link>/posts/easyanticheat-cr3-protection/</link>
<pubDate>Thu, 13 Apr 2023 00:00:00 +0000</pubDate>
<guid>/posts/easyanticheat-cr3-protection/</guid>
<description>Disclaimer The information provided in this document is intended solely for educational and informational purposes. It is not meant to belittle EasyAntiCheat or any individuals involved in its development or implementation. Rather, it aims to shed light on the internal workings of EasyAntiCheat so that consumers can better understand what happens behind the scenes when playing their favorite games. Any opinions expressed herein do not necessarily reflect those of EasyAntiCheat or any other parties mentioned.</description>
<content><h1 id="disclaimer">Disclaimer</h1>
<p>The information provided in this document is intended <strong>solely for educational and informational purposes</strong>. It is <strong>not meant to belittle EasyAntiCheat</strong> or any individuals involved in its development or implementation. Rather, it aims to shed light on the internal workings of EasyAntiCheat so that consumers can better understand what happens behind the scenes when playing their favorite games. Any opinions expressed herein <strong>do not necessarily reflect those of EasyAntiCheat</strong> or any other parties mentioned. This document is provided &ldquo;<strong>as is</strong>&rdquo; without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and fitness for a particular purpose. I shall not be liable for any damages whatsoever arising out of or in connection with the use of this document.</p>
<h1 id="introduction">Introduction</h1>
<p>The world of online gaming has long been plagued by the scourge of cheaters, whose insidious machinations threaten to undermine the very foundations upon which fair play and competition are built. But fear not, for there is a shining beacon of hope in this dark and murky landscape - EasyAntiCheat. Employed by some of the most popular titles on the market today, this cutting-edge software represents a formidable bulwark against those who would seek to gain an unfair advantage through illicit means. But just how does it work? What makes it so effective? And can it truly stand up to the ever-evolving tactics of the cheating underworld? Join us as we delve deep into the heart of this technological marvel and uncover the secrets that make it a force to be reckoned with.</p>
<h1 id="before-we-dive-in-getting-the-basics-right">Before We Dive In: Getting the Basics Right</h1>
<p>To proceed with the remaining part of the article, I recommend first addressing these topics:</p>
<ul>
<li><a href="https://www.triplefault.io/2017/07/introduction-to-ia-32e-hardware-paging.html">IA-32e Hardware Paging</a></li>
<li><a href="https://www.triplefault.io/2017/08/exploring-windows-virtual-memory.html">Windows Virtual Memory Management</a></li>
<li><a href="https://en.wikipedia.org/wiki/Process_isolation">Process Isolation</a></li>
</ul>
<h1 id="identifying-the-problem">Identifying the Problem</h1>
<p>As any individual who has engaged in cheating will attest, accessing memory is a pivotal aspect of the process. However, with anti-cheat measures operating at the kernel level, cheaters have had to resort to executing at this elevated level as well; the highest level under <code>SMM</code> and <code>VT-X/AMD-V</code>.</p>
<p>As anti-cheats have evolved, they have adopted increasingly sophisticated strategies that delve deep into the intricacies of the Windows kernel. Take, for instance, Vanguard, the anti-cheat system used in Valorant. It safeguards critical game regions by utilizing a technique to hook context swaps and creating a whitelist of specific threads that are authorized to access their cloned <code>CR3</code>; which allows for seamless, yet secure, access to the protected memory.</p>
<p>Such measures have proven especially effective in thwarting DMA cheats, which leverage an external device to translate virtual memory to its corresponding physical memory mappings and extract data.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>VOID VgkHooks<span style="color:#f92672">::</span>PostSwapContext( PVOID Thread )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> UINT64 ThreadIndex <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span> BOOLEAN AllowCr3Write <span style="color:#f92672">=</span> FALSE;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// I have simplified it here, this routine actually decrypts the obfuscated import.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">auto</span> PsGetThreadProcess <span style="color:#f92672">=</span> Vgk<span style="color:#f92672">::</span>Imports<span style="color:#f92672">::</span>PsGetThreadProcess;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Ensure that we are swapping a thread associated with Valorant.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> ( __readcr3( ) <span style="color:#f92672">==</span> GuardedRegion.GameCr3 )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( PsGetThreadProcess( Thread ) <span style="color:#f92672">==</span> VgkData<span style="color:#f92672">::</span>ValorantProcess )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> _disable( );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Update the pml4s, Windows may have changed it.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vgk<span style="color:#f92672">::</span>Setup( GuardedRegion.OriginalPml4s );
</span></span><span style="display:flex;"><span> memcpy( GuardedRegion.ClonedPml4s, GuardedRegion.OriginalPml4s, PAGE_SIZE );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// As the pml4 table has been overwritten, it is necessary for us to reset our pml4e.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> GuardedRegion.ClonedPml4s[ GuardedRegion.AvailablePml4Index ] <span style="color:#f92672">=</span> GuardedRegion.NewPml4e;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Has the game surpassed the maximum allowable whitelisted thread count as permitted by VGK?
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> ( ThreadData.Count <span style="color:#f92672">!=</span> VGK_MAX_THREADS )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( <span style="color:#f92672">!</span>ThreadData.Count )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> WriteCr3:
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( AllowCr3Write )
</span></span><span style="display:flex;"><span> __writecr3( GuardedRegion.NewCr3 );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( ShouldFlushTlb )
</span></span><span style="display:flex;"><span> FlushTlb( );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> _enable( );
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// If not, enumerate through each whitelisted thread.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">while</span> ( Thread <span style="color:#f92672">!=</span> ThreadData.List[ ThreadIndex ] )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( ThreadIndex<span style="color:#f92672">++</span> <span style="color:#f92672">&gt;=</span> ThreadData.Count )
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">goto</span> WriteCr3;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> AllowWriteCr3 <span style="color:#f92672">=</span> TRUE;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">goto</span> WriteCr3;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>The code that is not relevant to this article has been redacted, and the remaining code has been formatted for improved readability.</p>
</blockquote>
<p>While this is certainly fascinating, the question remains: how does all of this relate to EasyAntiCheat? As it turns out, EasyAntiCheat is also quite adept at detecting and thwarting cheat attempts. In fact, it employs a technique that is similar to the aforementioned Vanguard anti-cheat, albeit one that is more complex in nature.</p>
<p>However, unlike Vanguard, which utilizes two legitimate address spaces, EasyAntiCheat opts for a different approach - one that involves concealing its original <code>CR3</code> from any prying eyes. This is an involved and intricate technique, but one that has proven to be quite effective in deterring cheaters.</p>
<blockquote>
<p>It&rsquo;s worth noting that <code>Rust</code>, the game detailed in this article, incorporates <code>EasyAntiCheat_EOS</code>. As far as my observation goes, no other EOS-protected game has implemented this particular approach.</p>
</blockquote>
<h1 id="explaining-the-problem">Explaining the Problem</h1>
<p>In order to provide additional support for the claims we have made, we&rsquo;ll translate Rust&rsquo;s base address to its corresponding physical mapping using the <code>CR3</code> located in the <code>EProcess::DirectoryTableBase</code> field. This will provide us with concrete evidence to support our claim, which is a critical step because if the <code>CR3</code> value is truly invalid, the translation process would fail as it would not be pointing to any legitimate <code>PML4</code> table.</p>
<p>To further validate our findings, we&rsquo;ll compare the results obtained through this process with the actual game&rsquo;s fixed <code>CR3</code>.</p>
<p>To simplify this task, and make the process more efficient, we&rsquo;ll utilize the well-known and widely-used <a href="https://www.cheatengine.org/">Cheat Engine</a> Lua scripting interface.</p>
<blockquote>
<p>I decided to use Cheat Engine because it offers scripting capabilities that can be quickly accessed and modified, rather than having to create a driver for every minor alteration.</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#66d9ef">local</span> RustProcess <span style="color:#f92672">=</span> dbk_getPEProcess( RustPid );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">local</span> RustSectionBaseAddress <span style="color:#f92672">=</span> readQword( RustProcess <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x520</span> );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">local</span> RustDirectoryTableBase <span style="color:#f92672">=</span> readQword( RustProcess <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x28</span> );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print( string.format( <span style="color:#e6db74">&#34;RustSectionBaseAddress -&gt; %X&#34;</span>, RustSectionBaseAddress ) );
</span></span><span style="display:flex;"><span>print( string.format( <span style="color:#e6db74">&#34;RustDirectoryTableBase -&gt; %X&#34;</span>, RustDirectoryTableBase ) );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">local</span> RustPhysicalBaseAddress <span style="color:#f92672">=</span> getPhysicalAddressCR3( RustDirectoryTableBase, RustSectionBaseAddress );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> RustPhysicalBaseAddress <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> print( <span style="color:#e6db74">&#34;Error -&gt; 0&#34;</span> );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span> print( string.format( <span style="color:#e6db74">&#34;Success -&gt; %X&#34;</span>, RustPhysicalBaseAddress ) );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">--[[
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> RustSectionBaseAddress -&gt; 7FF7B4600000
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> RustDirectoryTableBase -&gt; 4000000853DFF000
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> Error -&gt; 0
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">--]]</span>
</span></span></code></pre></div><p>Now, for the corresponding counterpart:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#66d9ef">local</span> RustProcess <span style="color:#f92672">=</span> dbk_getPEProcess( RustPid );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">local</span> RustSectionBaseAddress <span style="color:#f92672">=</span> readQword( RustProcess <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x520</span> );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">local</span> RustDirectoryTableBase <span style="color:#f92672">=</span> dbk_getCR3( );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print( string.format( <span style="color:#e6db74">&#34;RustSectionBaseAddress -&gt; %X&#34;</span>, RustSectionBaseAddress ) );
</span></span><span style="display:flex;"><span>print( string.format( <span style="color:#e6db74">&#34;RustDirectoryTableBase -&gt; %X&#34;</span>, RustDirectoryTableBase ) );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">local</span> RustPhysicalBaseAddress <span style="color:#f92672">=</span> getPhysicalAddressCR3( RustDirectoryTableBase, RustSectionBaseAddress );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> RustPhysicalBaseAddress <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> print( <span style="color:#e6db74">&#34;Error -&gt; 0&#34;</span> );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span> print( string.format( <span style="color:#e6db74">&#34;Success -&gt; %X&#34;</span>, RustPhysicalBaseAddress ) );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">--[[
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> RustSectionBaseAddress -&gt; 7FF7B4600000
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> RustDirectoryTableBase -&gt; 197198000
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> Success -&gt; 199CFE000
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">--]]</span>
</span></span></code></pre></div><p>As you can see, the <code>CR3</code> value <code>4000000853DFF000</code> appears quite peculiar at first glance. It&rsquo;s pretty obvious that something is amiss even before consulting the manual. To get a better understanding, let&rsquo;s use the programmer&rsquo;s calculator on Windows to examine the toggled bits in the 64-bit integer.</p>
<p><img src="https://i.imgur.com/nzYfRgr.png" alt=""></p>
<p>As observed from the calculator&rsquo;s output, the 63rd bit is set. Armed with this knowledge, we can now refer to the manual to determine if this is a reserved bit that would trigger any sort of exception.</p>
<ul>
<li>If an attempt is made to change <code>CR4.PCIDE</code> from 0 to 1 while <code>CR3[11:0]</code> ≠ <code>000H</code>.</li>
<li>If an attempt is made to clear <code>CR0.PG[bit 31]</code>.</li>
<li>If an attempt is made to write a 1 to any reserved bit in <code>CR4</code>.</li>
<li>If an attempt is made to write a 1 to any reserved bit in <code>CR8</code>.</li>
<li>If an attempt is made to write a 1 to any reserved bit in <code>CR3[63:MAXPHYADDR]</code>.</li>
<li>If an attempt is made to leave IA-32e mode by clearing <code>CR4.PAE[bit 5]</code>.</li>
</ul>
<p>Since our focus is on a particular control register, namely the third one, we can narrow down the scope of the search to indicate that triggering a <code>#GP(0)</code> exception would occur only if one tries to write a value of 1 to any reserved bit within <code>CR3[63:MAXPHYADDR]</code>, which is exactly what we&rsquo;re looking for!</p>
<p>At this point, you might be wondering why it&rsquo;s not possible to unset those bits to make the <code>CR3</code> valid. However, the <code>CR3</code> that&rsquo;s saved within Rust&rsquo;s process is solely intended to trigger an exception and doesn&rsquo;t refer to any <code>PML4s</code> (without proper decryption). This approach from EasyAntiCheat is quite clever as it compels reverse engineers, such as ourselves, to reverse their driver.</p>
<h1 id="connecting-the-dots-the-thread-scheduler">Connecting the Dots: The Thread Scheduler</h1>
<p>Now that we have obtained the prerequisite knowledge on the issue at hand, we can start connecting the dots to understand how EasyAntiCheat is exploiting the vulnerability.</p>
<p>As we already know, EasyAntiCheat forces an exception when the <code>EProcess::DirectoryTableBase</code> is being written to the <code>CR3</code> of any active processor. However, this raises the question: how exactly are they are abusing this?</p>
<p>To answer this question, let&rsquo;s take a closer look at the <code>ntoskrnl!SwapContext</code> routine, which is responsible for swapping the current core&rsquo;s context to a new thread.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// The impact of their hook extends beyond this specific routine and encompasses any kernel routine that involves CR3 swapping, such as KiAttachProcess.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>UINT64 ProcessCr3 <span style="color:#f92672">=</span> Process<span style="color:#f92672">-&gt;</span>DirectoryTableBase;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> ( ( HvlEnlightenments <span style="color:#f92672">&amp;</span> <span style="color:#ae81ff">1</span> ) <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span> )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// If HyperV is present, it&#39;ll handle swapping the current core&#39;s CR3.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> HvlSwitchVirtualAddressSpace( ProcessCr3 );
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> __writecr3( ProcessCr3 );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( ShouldFlushTlb )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// If possible, flushes the TLB for the current core.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> Cr4 <span style="color:#f92672">=</span> __readcr4( );
</span></span><span style="display:flex;"><span> Cr4 <span style="color:#f92672">^=</span> <span style="color:#ae81ff">0x80</span>;
</span></span><span style="display:flex;"><span> __writecr4( Cr4 );
</span></span><span style="display:flex;"><span> __writecr4( Cr4 <span style="color:#f92672">^</span> <span style="color:#ae81ff">0x80</span> );
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>The code that is not relevant to this article has been redacted, and the remaining code has been formatted for improved readability.</p>
</blockquote>
<p>By analyzing the code within this routine, we can see that the function proceeds to update the <code>CR3</code> register to the <code>EProcess::DirectoryTableBase</code> field using the <code>__writecr3</code> intrinsic.
It is at this point where EasyAntiCheat is able to exploit the vulnerability. By forcing an exception, and catching it, EasyAntiCheat is able to instrument when a thread is being swapped, and thereby obtain complete and utter control over their game&rsquo;s context-switches (and other whitelisted regions).</p>
<h1 id="connecting-the-dots-wresling-the-exception">Connecting the Dots: Wresling the Exception</h1>
<p>Following the reverse engineering process, the next step is to locate the location where EasyAntiCheat writes the updated <code>CR3</code>. Based on our previous findings, this will be found within their exception hook.</p>
<p>Although there are multiple methods to achieve this, the steps I will take include:</p>
<ul>
<li>Utilizing my <code>VT-X</code> hypervisor to virtualize all logical processors.</li>
<li>Installing an image load callback.</li>
<li>Upon the loading of the <code>EasyAntiCheat_EOS.sys</code> driver, preserving the driver&rsquo;s details.</li>
<li>Deferring a task to the subsequent <code>CR3</code> write, if an attempt is made to write a 1 to any reserved bit in <code>CR3[63:MAXPHYADDR]</code>.</li>
<li>Recording the <code>RVA</code> using EasyAntiCheat&rsquo;s stored driver information.</li>
</ul>
<p>Upon completion of the aforementioned steps, and removing duplicates for clarity, the outcomes obtained are:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{<span style="color:#f92672">&#34;Type&#34;</span>:<span style="color:#e6db74">&#34;Invalid&#34;</span>,<span style="color:#f92672">&#34;RVA&#34;</span>:<span style="color:#e6db74">&#34;ntoskrnl.exe+0x40028F&#34;</span>}
</span></span><span style="display:flex;"><span>{<span style="color:#f92672">&#34;Type&#34;</span>:<span style="color:#e6db74">&#34;Valid&#34;</span>,<span style="color:#f92672">&#34;RVA&#34;</span>:<span style="color:#e6db74">&#34;EasyAntiCheat_EOS.sys+0x19A20&#34;</span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{<span style="color:#f92672">&#34;Type&#34;</span>:<span style="color:#e6db74">&#34;Invalid&#34;</span>,<span style="color:#f92672">&#34;RVA&#34;</span>:<span style="color:#e6db74">&#34;ntoskrnl.exe+0x20C130&#34;</span>}
</span></span><span style="display:flex;"><span>{<span style="color:#f92672">&#34;Type&#34;</span>:<span style="color:#e6db74">&#34;Valid&#34;</span>,<span style="color:#f92672">&#34;RVA&#34;</span>:<span style="color:#e6db74">&#34;EasyAntiCheat_EOS.sys+0x19A20&#34;</span>}
</span></span></code></pre></div><p>Although it may appear daunting initially, it is not something we have not previously discussed. Let&rsquo;s review it together.</p>
<p>First, let us direct our attention towards the <code>Invalid</code> outcomes, which occur when the kernel writes EasyAntiCheat&rsquo;s exception-forced <code>CR3</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-asm" data-lang="asm"><span style="display:flex;"><span>.text:<span style="color:#960050;background-color:#1e0010">000000000040028</span><span style="color:#a6e22e">F</span> <span style="color:#ae81ff">0</span><span style="color:#66d9ef">F</span> <span style="color:#ae81ff">22</span> <span style="color:#66d9ef">D9</span> <span style="color:#66d9ef">mov</span> <span style="color:#66d9ef">cr3</span>, <span style="color:#66d9ef">rcx</span> <span style="color:#75715e">; SwapContext
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-asm" data-lang="asm"><span style="display:flex;"><span>.text:<span style="color:#960050;background-color:#1e0010">000000000020</span><span style="color:#a6e22e">C130</span> <span style="color:#ae81ff">0</span><span style="color:#66d9ef">F</span> <span style="color:#ae81ff">22</span> <span style="color:#66d9ef">DF</span> <span style="color:#66d9ef">mov</span> <span style="color:#66d9ef">cr3</span>, <span style="color:#66d9ef">rdi</span> <span style="color:#75715e">; KiAttachProcess
</span></span></span></code></pre></div><p>As you can observe, we have previously covered this, it&rsquo;s where the kernel writes Rust&rsquo;s <code>CR3</code>. Let&rsquo;s now shift our focus towards the juicy <code>Valid</code> outcomes, where EasyAntiCheat writes the genuine <code>CR3</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>BOOLEAN EacHooks<span style="color:#f92672">::</span>HandleException( ExceptonData<span style="color:#f92672">*</span> Exception, PCONTEXT Context )
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#75715e">#define GetFixedCr3( Key ) ((__ROR8__(_byteswap_uint64(Key), 31) &amp; 0xFFFFFFFFF) &lt;&lt; 12)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( Exception<span style="color:#f92672">-&gt;</span>Code <span style="color:#f92672">==</span> STATUS_PRIVILEGED_INSTRUCTION )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// --&gt; &#34;mov cr3&#34;, ??
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> ( <span style="color:#f92672">*</span>( WORD<span style="color:#f92672">*</span> )Context<span style="color:#f92672">-&gt;</span>Rip <span style="color:#f92672">==</span> <span style="color:#ae81ff">0x220F</span> )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// mov cr3, &#34;??&#34; &lt;--
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> BYTE Operand <span style="color:#f92672">=</span> <span style="color:#f92672">*</span>( BYTE<span style="color:#f92672">*</span> )( Context<span style="color:#f92672">-&gt;</span>Rip <span style="color:#f92672">+</span> <span style="color:#ae81ff">2</span> );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Converts the operand to an offset in the context structure, beginning from RAX.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Operand <span style="color:#f92672">&amp;=</span> <span style="color:#ae81ff">7</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Retrieve the CR3 that was being written from its register.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> UINT64<span style="color:#f92672">*</span> Registers <span style="color:#f92672">=</span> <span style="color:#f92672">&amp;</span>Context<span style="color:#f92672">-&gt;</span>Rax;
</span></span><span style="display:flex;"><span> UINT64 AttemptedCr3 <span style="color:#f92672">=</span> Registers[ Operand ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// This is always computes to the same value, which is the base of their structure&#39;s allocation.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> UINT64 DataOffset <span style="color:#f92672">=</span> InterlockedExchangeAdd64( EAC<span style="color:#f92672">::</span>InitialDataOffset, <span style="color:#ae81ff">0x1000000000</span> );
</span></span><span style="display:flex;"><span> DataOffset <span style="color:#f92672">+=</span> <span style="color:#ae81ff">0x1000000000</span>;
</span></span><span style="display:flex;"><span> DataOffset <span style="color:#f92672">&amp;=</span> <span style="color:#ae81ff">0xFFFFFFFFF</span>;
</span></span><span style="display:flex;"><span> DataOffset <span style="color:#f92672">&lt;&lt;=</span> <span style="color:#ae81ff">12</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// In their actual code, this uses an address in the stack to perform their calculation against.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> EAC<span style="color:#f92672">::</span>EacData<span style="color:#f92672">*</span> Data <span style="color:#f92672">=</span> ( EAC<span style="color:#f92672">::</span>EacData<span style="color:#f92672">*</span> )( ( <span style="color:#ae81ff">0xFFFFull</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">48</span> ) <span style="color:#f92672">+</span> DataOffset );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Nothing complicated here, just gets the current process.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> PEPROCESS CurrentProcess <span style="color:#f92672">=</span> <span style="color:#f92672">*</span>( PEPROCESS<span style="color:#f92672">*</span> )( UINT64( KeGetCurrentThread( ) ) <span style="color:#f92672">+</span> EAC<span style="color:#f92672">::</span>ProcessOffset );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// This isn&#39;t exactly what&#39;s done here, I&#39;ve simplified it.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> ( CurrentProcess <span style="color:#f92672">!=</span> Data<span style="color:#f92672">-&gt;</span>Process )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( AttemptedCr3 <span style="color:#f92672">!=</span> Data<span style="color:#f92672">-&gt;</span>Cr3 )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> InterlockedIncrement( Data<span style="color:#f92672">-&gt;</span>Counter );
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> FALSE;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> __writecr3( __readcr3( ) );
</span></span><span style="display:flex;"><span> Context<span style="color:#f92672">-&gt;</span>Rip <span style="color:#f92672">+=</span> <span style="color:#ae81ff">3</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> InterlockedIncrement( Data<span style="color:#f92672">-&gt;</span>Counter );
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> TRUE;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ( Context<span style="color:#f92672">-&gt;</span>Rip <span style="color:#f92672">&gt;=</span> EAC<span style="color:#f92672">::</span>WhitelistStart <span style="color:#f92672">&amp;&amp;</span> Context<span style="color:#f92672">-&gt;</span>Rip <span style="color:#f92672">&lt;</span> EAC<span style="color:#f92672">::</span>WhitelistEnd )
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// This removes the reserved bits, and fixes the CR3.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// The decryption changes per update, so don&#39;t expect this to remain.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> UINT64 FixedCr3 <span style="color:#f92672">=</span> AttemptedCr3 <span style="color:#f92672">&amp;</span> <span style="color:#ae81ff">0xBFFF000000000FFF</span>;
</span></span><span style="display:flex;"><span> FixedCr3 <span style="color:#f92672">|=</span> GetFixedCr3( Data<span style="color:#f92672">-&gt;</span>Key );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> __writecr3( FixedCr3 );
</span></span><span style="display:flex;"><span> Context<span style="color:#f92672">-&gt;</span>Rip <span style="color:#f92672">+=</span> <span style="color:#ae81ff">3</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> InterlockedIncrement( Data<span style="color:#f92672">-&gt;</span>Counter );
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> TRUE;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> FALSE;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>The code that is not relevant to this article has been redacted, and the remaining code has been formatted for improved readability.</p>
</blockquote>
<p>Although I have improved the code&rsquo;s readability by symbolizing and cleaning it, I will still provide a summary of its behavior:</p>
<ul>
<li>Verify that the exception resulted from a <code>mov cr3, ??</code> instruction.</li>
<li>Extract the instruction&rsquo;s operand and convert it to an index beginning from <code>ZERO/RAX</code>.</li>
<li>Retrieve the value of the <code>CR3</code> register from the the context structure.</li>
<li>Compute the address by calculating the offset to the hook&rsquo;s data structure.</li>
<li>Verify that the exception occurred in Rust&rsquo;s process and check the validity of the <code>CR3</code>.</li>
<li>Verify that the exception occurred within a designated region inside <code>ntoskrnl.exe</code>.</li>
<li>If all of the above conditions are met, update <code>CR3</code> and <code>RIP</code> accordingly.</li>
</ul>
<p>Awesome! You know the drill by now, let&rsquo;s confirm our reversal by comparing Rust&rsquo;s process with the structure.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#66d9ef">local</span> InitialDataOffset <span style="color:#f92672">=</span> readQword( EacBase <span style="color:#f92672">+</span> EacInitialDataOffset );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">local</span> DataOffset <span style="color:#f92672">=</span> bAnd( InitialDataOffset, <span style="color:#ae81ff">0xFFFFFFFFF</span> );
</span></span><span style="display:flex;"><span>DataOffset <span style="color:#f92672">=</span> bShl( DataOffset, <span style="color:#ae81ff">12</span> );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">local</span> Data <span style="color:#f92672">=</span> bOr( bShl( <span style="color:#ae81ff">0xFFFF</span>, <span style="color:#ae81ff">48</span> ), DataOffset );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> ( readQword( Data <span style="color:#f92672">+</span> <span style="color:#ae81ff">0xC</span> ) <span style="color:#f92672">==</span> dbk_getPEProcess( RustPid ) ) <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span> print( <span style="color:#e6db74">&#34;Valid Structure&#34;</span> );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span> print( <span style="color:#e6db74">&#34;Invalid Structure&#34;</span> );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">--[[
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"> Valid Structure
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">--]]</span>
</span></span></code></pre></div><h1 id="connecting-the-dots-hal-to-the-rescue">Connecting the Dots: Hal to the Rescue!</h1>
<p>Up until now, we have discovered that EasyAntiCheat is somehow able to intercept any exception generated, but we don&rsquo;t know how. So, let&rsquo;s find out!</p>
<p>While there are multiple approaches to this problem, such as recursing and reversing through every function from the interrupt&rsquo;s routine. I opted for a more suitable method, which involved tracking the return stack directly to the function.</p>
<p>To achieve this, I set a software breakpoint, <code>INT3</code>, on their exception dispatcher routine and read the guest&rsquo;s <code>RSP</code> from the <code>VMCS</code>. Then, I walked the stack and checked if the code was within a kernel code section.</p>
<p>After tracing where it led me, I came to the conclusion that EasyAntiCheat was hooking <code>Hal</code> pointers, as the return address led to the return of a call to a <code>Hal</code> callback.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>InternalData <span style="color:#f92672">=</span> HalpTimerGetInternalData( Timer );
</span></span><span style="display:flex;"><span>Rax <span style="color:#f92672">=</span> ( <span style="color:#f92672">*</span>( <span style="color:#66d9ef">__int64</span> ( <span style="color:#66d9ef">__fastcall</span> <span style="color:#f92672">**</span> )( <span style="color:#66d9ef">__int64</span> ) )( Timer <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x70</span> ) )( InternalData );
</span></span></code></pre></div><p>This can be easily verified this by running this script and observing that all pointers point to EasyAntiCheat&rsquo;s <code>Hal</code> dispatcher:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#66d9ef">local</span> Timer <span style="color:#f92672">=</span> readPointer( HalpRegisteredTimers );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">while</span> Timer <span style="color:#f92672">~=</span> HalpRegisteredTimers <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span> print( string.format( <span style="color:#e6db74">&#34;Timer %X points to Function %X&#34;</span>, Timer, readPointer( Timer <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x70</span> ) ) );
</span></span><span style="display:flex;"><span> Timer <span style="color:#f92672">=</span> readPointer( Timer );
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p><strong>Congratulations on making it this far, that&rsquo;s really impressive!</strong></p>
<p>Now, I have a little brain-teaser for you. Based on the information I&rsquo;ve given you previously, try to figure out how EasyAntiCheat manages its <code>Hal</code> hooks by reversing their dispatcher.</p>
<h1 id="building-the-puzzle-breaking-the-wall">Building the Puzzle: Breaking the Wall</h1>
<p>I hope this article has been helpful in showing how anticheats can exploit the kernel to their advantage. However, this is just the beginning, as it&rsquo;s likely that EasyAntiCheat will eventually start hooking syscalls from their driver - Vanguard has been doing it for a while already.</p>
<p>Furthermore, with EasyAntiCheat&rsquo;s full control of context swaps, they can even implement per-thread hooks that are invisible to external threads. Alternatively, they can create hidden code regions that change on a per-thread basis.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// This changes per update, it&#39;s very simple to copy.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#define DecryptCr3( Cr3 )
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// We don&#39;t need to increment it, it&#39;s useless.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>UINT64 DataOffset <span style="color:#f92672">=</span> ( InitialDataOffset <span style="color:#f92672">&amp;</span> <span style="color:#ae81ff">0xFFFFFFFFF</span> ) <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">12</span>;
</span></span><span style="display:flex;"><span>UINT64 Data <span style="color:#f92672">=</span> <span style="color:#f92672">*</span>( UINT64<span style="color:#f92672">*</span> )( ( <span style="color:#ae81ff">0xFFFFull</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">48</span> ) <span style="color:#f92672">+</span> DataOffset );
</span></span><span style="display:flex;"><span>DbgPrint( <span style="color:#e6db74">&#34;[Eac] Data -&gt; %llx</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>, Data );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>PEPROCESS RustProcess;
</span></span><span style="display:flex;"><span>PsLookupProcessByProcessId( RustPid, <span style="color:#f92672">&amp;</span>RustProcess );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>UINT64 FakeCr3 <span style="color:#f92672">=</span> <span style="color:#f92672">*</span>( UINT64<span style="color:#f92672">*</span> )( UINT64( RustProcess ) <span style="color:#f92672">+</span> <span style="color:#ae81ff">0x28</span> );
</span></span><span style="display:flex;"><span>UINT64 FixedCr3 <span style="color:#f92672">=</span> DecryptCr3( FakeCr3 <span style="color:#f92672">&amp;</span> <span style="color:#ae81ff">0xBFFF000000000FFF</span> );
</span></span><span style="display:flex;"><span>DbgPrint( <span style="color:#e6db74">&#34;[Eac] FixedCr3 -&gt; %llx</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>, FixedCr3 );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// We don&#39;t want to leak any memory.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>ObDereferenceObject( RustProcess );
</span></span></code></pre></div></content>
</item>
<item>
<title></title>
<link>/about/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>/about/</guid>
<description>About Me I engage in my research as a hobby, and do not pursue it for monetary gain.
Don&rsquo;t contact me for cheating related inquires, as I will not respond.</description>
<content><h1 id="about-me">About Me</h1>
<p>I engage in my research as a hobby, and do not pursue it for monetary gain.</p>
<p>Don&rsquo;t contact me for cheating related inquires, as I will not respond.</p>
</content>
</item>
</channel>
</rss>