<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>codeneverdies!</title>
    <description>There&apos;s nothing new under the sun.</description>
    <link>https://codeneverdies.github.io/</link>
    <atom:link href="https://codeneverdies.github.io/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Anti-Cheat: Stopping Memory Acquisition via Crash Dumps</title>
        <description>&lt;p&gt;It’s no secret that cheaters and security researchers want to get their hands on anti-cheat software. Many anti-cheat developers virtualize and pack their binaries, making static analysis harder. This forces adversaries to analyze the anti-cheat software dynamically, which could entail stepping through the unpacking process manually then dumping the anti-cheat driver from memory. Attaching a kernel debugger can also be a problem due to anti-debugging checks, so how are people dumping anti-cheat drivers? And how could they prevent this in the future? That leads us to our topic today.&lt;/p&gt;

&lt;h1 id=&quot;memory-acquisition-via-crash-dumps&quot;&gt;Memory Acquisition via Crash Dumps&lt;/h1&gt;

&lt;p&gt;During a quick chat with a seasoned game hacker, dumping anti-cheat drivers from crash dumps has been around for a while.
If the adversary isn’t skilled enough to conduct a BYOVD attack and dump the driver, they can resort to this easier method. Here’s some external research
to help you understand what this article will be about. On april 7th, 2023 a clever person on the guided hacking website named &lt;a href=&quot;https://guidedhacking.com/members/mrmoo.256455/&quot;&gt;MrMoo&lt;/a&gt; showed us &lt;a href=&quot;https://guidedhacking.com/threads/how-to-extract-ea-anticheat-driver-via-crash-dumps.20328/&quot;&gt;How To Dump EA’s Driver&lt;/a&gt; by intentionally crashing the system. Once the system rebooted he analyzed the kernel crash dump with volatility to dump the anti-cheat driver. June 7th, 2024 a user on unknowncheats &lt;a href=&quot;https://www.unknowncheats.me/forum/members/5120071.html&quot;&gt;LabGuy94&lt;/a&gt; posted about the &lt;a href=&quot;https://www.unknowncheats.me/forum/anti-cheat-bypass/640987-abusing-windows-complete-memory-crash-dumps.html&quot;&gt;same technique&lt;/a&gt;. Two years later on march 5th, 2026 a researcher named &lt;a href=&quot;https://x.com/s4dbrd&quot;&gt;s4dbrd&lt;/a&gt; &lt;a href=&quot;https://s4dbrd.github.io/posts/reversing-bedaisy/&quot;&gt;published&lt;/a&gt; his analysis on BattlEye’s anti-cheat driver utilizing this technique and NotMyFault to dump their driver from memory. Finally on june 16th, 2026 I &lt;a href=&quot;https://guidedhacking.com/threads/how-to-dump-anti-cheat-kernel-drivers.21328/&quot;&gt;published&lt;/a&gt; a tutorial on the guided hacking showcasing how you can crash the system with and without NotMyFault. After the kernel memory dump was generated, I transfered it to my host machine and dumped the anti-cheat driver from the crash dump using windbg. One thing these all have in common is that they generate a bug check (BSOD). It may seem like this is a full proof method of dumping the anti-cheat because once the system is crashing the anti-cheat has no control. I like to think that this is far from the truth, the rest of this article aims to back that up and show you how this can be stopped.&lt;/p&gt;

&lt;h1 id=&quot;stopping-memory-acquisition-via-crash-dumps&quot;&gt;Stopping Memory Acquisition via Crash Dumps&lt;/h1&gt;

&lt;p&gt;Our journey starts at the manually initiated crash screen, we know that users are crashing their systems on purpose and a common bug check code seen is &lt;code&gt;MANUALLY_INITIATED_CRASH&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH3/4.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we search for this bug check code and snoop around MSDN, we’ll that find Microsoft allows drivers to register callback functions that run when the system bug checks and starts to generate the kernel crash dump. A driver can register a bug check callback routine by calling &lt;code&gt;KeRegisterBugCheckCallback&lt;/code&gt; or &lt;code&gt;KeRegisterBugCheckReasonCallback&lt;/code&gt;. &lt;code&gt;KeRegisterBugCheckReasonCallback&lt;/code&gt; is what we’ll be using, this function is useful because in the &lt;code&gt;KBUGCHECK_CALLBACK_REASON Reason&lt;/code&gt; argument we can specify &lt;code&gt;KbCallbackRemovePages&lt;/code&gt;. This enum value tells the windows kernel that our callback will be removing pages from the crash dump. I’m sure you can do something with the &lt;code&gt;KbCallbackDumpIo&lt;/code&gt; type as well but I feel that my method is more straight forward. When you’re writing bug check callbacks you’re pretty restricted from what you can do so it’s best to keep it simple. A bug check callback with type &lt;code&gt;KbCallbackDumpIo&lt;/code&gt; can be called multiple times during the crash, so I just played it safe.&lt;/p&gt;

&lt;h2 id=&quot;about-bug-check-callback-routines&quot;&gt;About Bug Check Callback Routines&lt;/h2&gt;

&lt;p&gt;To understand more about what we’re doing let’s look at the implementation of &lt;code&gt;KeRegisterBugCheckReasonCallback&lt;/code&gt;, and other related data structures.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;// https://doxygen.reactos.org/d3/d13/bug_8c.html#a73fca11ec74d2871e8981bbcd00f0f24
BOOLEAN KeRegisterBugCheckReasonCallback(PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord, PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine, KBUGCHECK_CALLBACK_REASON Reason, PUCHAR Component) {
    
    KIRQL OldIrql;
    BOOLEAN Status = FALSE;
    KeRaiseIrql(HIGH_LEVEL, &amp;amp;OldIrql);

    if ( CallbackRecord-&amp;gt;State == BufferEmpty ) {
        CallbackRecord-&amp;gt;Component = Component;
        CallbackRecord-&amp;gt;CallbackRoutine = CallbackRoutine;
        CallbackRecord-&amp;gt;State = BufferInserted;
        CallbackRecord-&amp;gt;Reason = Reason;
        InsertTailList(&amp;amp;KeBugcheckReasonCallbackListHead,&amp;amp;CallbackRecord-&amp;gt;Entry);
        Status = TRUE;
    }

    KeLowerIrql(OldIrql);
    return Status;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First &lt;code&gt;KeRegisterBugCheckReasonCallback&lt;/code&gt; raises the IRQL to &lt;code&gt;HIGH_LEVEL&lt;/code&gt;, then it checks if the callback record was initialized by &lt;code&gt;KeInitializeCallbackRecord&lt;/code&gt; (This is just a macro for &lt;code&gt;PKBUGCHECK_REASON_CALLBACK_RECORD-&amp;gt;State = BufferEmpty&lt;/code&gt;). If it was then the callback record is initialized, and it gets inserted into the &lt;code&gt;KeBugcheckReasonCallbackList&lt;/code&gt;. In our case it should be in the &lt;code&gt;KeBugCheckAddRemovePagesCallbackList&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But wait there’s two more bug check lists:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;KeBugcheckCallbackListHead&lt;/code&gt;: Used by callbacks registered by &lt;code&gt;KeRegisterBugCheckCallback&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;KeBugCheckTriageDumpDataArrayListHead&lt;/code&gt;: Used by callbacks of type &lt;code&gt;KbCallbackTriageDumpData&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe you can remove a callback from these lists? I’m not sure if these are protected by patch guard (KPP) or not.&lt;/p&gt;

&lt;h2 id=&quot;hypervisor-detection-using-bug-check-callbacks&quot;&gt;Hypervisor Detection Using Bug Check Callbacks&lt;/h2&gt;

&lt;p&gt;To give you more insight on how other people have used bug check callbacks in the anti-cheat space, let’s go back six years. On april 13, 2020 members from &lt;a href=&quot;https://secret.club&quot;&gt;secret.club&lt;/a&gt; (daax, iPower, ajkhoury and drew) wrote about how anti-cheats detect system emulation. In the section named &lt;a href=&quot;https://secret.club/2020/04/13/how-anti-cheats-detect-system-emulation.html#xsetbv&quot;&gt;XSETBV&lt;/a&gt;, they talk about using the &lt;code&gt;XSETBV&lt;/code&gt; instruction to intentionally generate a fault that will cause a VM-exit. Majority of public hypervisors used to analyze anti-cheat software will handle this in the host’s &lt;code&gt;XSETBV&lt;/code&gt; handler. Because of this and the driver not being able to handle SEH (it’s manually mapped), the host will bug check (BSOD). This is why they registered a bug check callback routine using &lt;code&gt;KbCallbackSecondaryDumpData&lt;/code&gt;, to add data to the crash dump and uniquely identify their crash data. Today we’ll be removing data from the crash dump.&lt;/p&gt;

&lt;h1 id=&quot;removing-the-anti-cheat-driver-from-the-crash-dump&quot;&gt;Removing The Anti-Cheat Driver From The Crash Dump&lt;/h1&gt;

&lt;p&gt;Before we get started here’s a little recap/road map, we’ll call &lt;code&gt;KeRegisterBugCheckReasonCallback&lt;/code&gt; to register our bug check reason callback so we can run code when the system bug checks. When calling this function we will supply &lt;code&gt;KbCallbackRemovePages&lt;/code&gt; in the &lt;code&gt;Reason&lt;/code&gt; argument, so the windows kernel knows that our callback will remove pages from the dump. Here’s how I registered my bug check callback.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;callback_record = ExAllocatePoolWithTag(NonPagedPool, sizeof(KBUGCHECK_REASON_CALLBACK_RECORD), CARDINAL_MEM_TAG);
if (!callback_record)
    return;

cardinal_get_info();
UCHAR component[] = &quot;Bugcheckin\n&quot;;
KeInitializeCallbackRecord(callback_record);
KeRegisterBugCheckReasonCallback(callback_record, cardinal_bugcheck_cb, KbCallbackRemovePages, component);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each bug check reason callback routine has the following prototype.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;typedef VOID (*bugcheck_cb_t)(KBUGCHECK_CALLBACK_REASON Reason, PKBUGCHECK_REASON_CALLBACK_RECORD Record, PVOID ReasonSpecificData, ULONG ReasonSpecificDataLength)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;Reason&lt;/code&gt; will be one of the enum values for example.. &lt;code&gt;KbCallbackDumpIo&lt;/code&gt;, &lt;code&gt;KbCallbackRemovePages&lt;/code&gt;, &lt;code&gt;KbCallbackAddPages&lt;/code&gt;, etc. Depending on the &lt;code&gt;Reason&lt;/code&gt;, &lt;code&gt;ReasonSpecificData&lt;/code&gt; can be cast to many other data structures. In our case we’ll be casting it to &lt;code&gt;KBUGCHECK_REMOVE_PAGES&lt;/code&gt;. When this callback routine is called, the kernel will have already populated the &lt;code&gt;BugCheckCode&lt;/code&gt; member of this struct. We’ll use that to determine what kind of crash it was.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;VOID cardinal_bugcheck_cb(KBUGCHECK_CALLBACK_REASON Reason, PKBUGCHECK_REASON_CALLBACK_RECORD Record, PVOID ReasonSpecificData, ULONG ReasonSpecificDataLength) {
    PKBUGCHECK_REMOVE_PAGES bugcheck = (PKBUGCHECK_REMOVE_PAGES)ReasonSpecificData;
    if ( // Bug checks intentionally generated by the keyboard and NotMyFault
        bugcheck-&amp;gt;BugCheckCode == MANUALLY_INITIATED_CRASH ||
        bugcheck-&amp;gt;BugCheckCode == KERNEL_MODE_HEAP_CORRUPTION ||
        bugcheck-&amp;gt;BugCheckCode == PAGE_FAULT_IN_NONPAGED_AREA ||
        bugcheck-&amp;gt;BugCheckCode == DRIVER_OVERRAN_STACK_BUFFER ||
        bugcheck-&amp;gt;BugCheckCode == DRIVER_IRQL_NOT_LESS_OR_EQUAL
    [..snip..]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If the bug check code was one of the following, then we can start to remove the anti-cheat driver from the crash dump. To do this we need to populate three more members of the &lt;code&gt;KBUGCHECK_REMOVE_PAGES&lt;/code&gt; struct. First, &lt;code&gt;KBUGCHECK_REMOVE_PAGES.Flags&lt;/code&gt; this member determines if the address that we supply next is going to be a virtual or physical address. I’ll be setting the &lt;code&gt;KB_REMOVE_PAGES_FLAG_VIRTUAL_ADDRESS&lt;/code&gt; flag.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;bugcheck-&amp;gt;Flags |= KB_REMOVE_PAGES_FLAG_VIRTUAL_ADDRESS;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To finalize removing the anti-cheat from the crash dump, we need to populate two more members. First &lt;code&gt;KBUGCHECK_REMOVE_PAGES.Address&lt;/code&gt;, this tells the windows kernel where in the crash dump to start removing pages. This will be set to the base address of our driver. Second &lt;code&gt;KBUGCHECK_REMOVE_PAGES.Count&lt;/code&gt;, this determines how many pages need to be removed from the start address. To populate both of these members I wrote a function that locates the base address and size of the anti-cheat driver, then calculates how many pages need to be removed from the crash dump.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;VOID cardinal_get_info() {

    [..snip..]

    PKLDR_DATA_TABLE_ENTRY entry = (PKLDR_DATA_TABLE_ENTRY)PsLoadedModuleList-&amp;gt;Flink;
    while ( (PLIST_ENTRY)entry != PsLoadedModuleList ) {

        if ( !RtlCompareUnicodeString(&amp;amp;entry-&amp;gt;BaseDllName, &amp;amp;drv_name, TRUE) ) {
            base = (ULONG_PTR)entry-&amp;gt;DllBase;
            size = (ULONG_PTR)entry-&amp;gt;SizeOfImage;
            break;
        }
        
        entry = (PKLDR_DATA_TABLE_ENTRY)entry-&amp;gt;InLoadOrderLinks.Flink;
    }

    cardinal_pg_sz = (size + PAGE_SIZE - 1) / PAGE_SIZE;
    cardinal_base = base;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once these global variables are populated we can populate the members inside of our bug check callback.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;bugcheck-&amp;gt;Address = cardinal_base;
bugcheck-&amp;gt;Count = cardinal_pg_sz;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now if we intentionally crash the system while the anti-cheat driver is loaded, we won’t be able to dump it to disk because the driver’s pages we’re removed from the crash dump! Rendering this method useless :). Let’s see this in action, I won’t be showing you how to crash the system you can find that information else where! What I did to test this was crash the system with the bug check callback registered. At first glance I didn’t think this worked because I was able to see it in the list of loaded modules (stupid of me), then I tried to view the base address of the driver in windbg’s Dissasembly tab and I was met with something interesting.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH3/2.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Trying to view the module in windbg also tells us that we can’t view the module’s info, because the pages aren’t present in the crash dump and that’s exactly what we want to hear :).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH3/3.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;bonus-detecting-crash-dump-registry-keys&quot;&gt;Bonus: Detecting Crash Dump Registry Keys&lt;/h1&gt;

&lt;p&gt;Instead of fighting during the crash, why not just check to see if the registy keys that enable these types of crashes are enabled? This way we don’t have to register a bug check callback routine if we don’t have to because we’ll know what to prepare for. The table below is a list of registry keys that can be used to bug check intentionally.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Path&lt;/th&gt;
      &lt;th&gt;Key&lt;/th&gt;
      &lt;th&gt;Value&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\crashdump&lt;/td&gt;
      &lt;td&gt;CrashOnCtrlScroll&lt;/td&gt;
      &lt;td&gt;0x01&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\kbdhid\crashdump&lt;/td&gt;
      &lt;td&gt;CrashOnCtrlScroll&lt;/td&gt;
      &lt;td&gt;0x01&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h1 id=&quot;remarks&quot;&gt;Remarks&lt;/h1&gt;

&lt;p&gt;I left out two methods of intentionally crashing the system from the bug check routine. I think it would be good for both sides to do the research themselves and find out what can be done next. I really enjoyed learning about this and I think that it’s a very niche technique. I can’t really see this being used anywhere else other than in rootkits and anti-cheats that are packed, and want to stunt analysis when they’re unpacked. You can view the full code for this article &lt;a href=&quot;https://github.com/codeneverdies/Cardinal&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;bug-check-references&quot;&gt;Bug Check References&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-keregisterbugcheckcallback&quot;&gt;KeRegisterBugCheckCallback&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-keregisterbugcheckreasoncallback&quot;&gt;KeRegisterBugCheckReasonCallback&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/writing-a-bug-check-callback-routine&quot;&gt;Writing a Bug Check Reason Callback Routine&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_kbugcheck_remove_pages&quot;&gt;KBUGCHECK_REMOVE_PAGES structure&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/bug-check-0xe2--manually-initiated-crash&quot;&gt;Bug Check 0xE2: MANUALLY_INITIATED_CRASH&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_kbugcheck_callback_reason&quot;&gt;KBUGCHECK_CALLBACK_REASON enumeration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate>
        <link>https://codeneverdies.github.io/2026/06/16/GH3/</link>
        <guid isPermaLink="true">https://codeneverdies.github.io/2026/06/16/GH3/</guid>
      </item>
    
      <item>
        <title>Anti-Cheat: Valve Anti-Cheat (VAC)</title>
        <description>&lt;p&gt;In 2002 Valve created an Anti-Cheat solution called “Valve Anti-Cheat” aka &lt;strong&gt;VAC&lt;/strong&gt;. 
The first game they implemented VAC into was Counter-Strike. When &lt;strong&gt;VAC&lt;/strong&gt; was introduced it only operated in 
User Mode (Still does) meaning it runs entirely in user space &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and has no kernel component.&lt;/p&gt;

&lt;p&gt;Below is a list of games that use &lt;strong&gt;VAC&lt;/strong&gt;..  &lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Call of Duty: Modern Warfare 2
Call of Duty: Modern Warfare 3
Counter-Strike (video game)
Counter-Strike: Condition Zero
Counter-Strike: Source
Counter-Strike 2
Day of Defeat
Day of Defeat: Source
Deathmatch Classic
Half-Life 2: Deathmatch
Half-Life Deathmatch: Source
Ricochet
Team Fortress
Team Fortress Classic
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A longer list can be found here &lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;vac-cident&quot;&gt;VAC-cident?&lt;/h1&gt;

&lt;p&gt;So.. if you don’t know &lt;strong&gt;VAC&lt;/strong&gt; has been around for quite a while, at the time of writing it’ll be 23 years. 
Over the time they’ve made some mistakes but who doesn’t? (Taken from wikipedia) &lt;sup id=&quot;fnref:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;sup id=&quot;fnref:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- In July 2010, [snip] Approximately 12,000 owners of Call of Duty: Modern Warfare 2 were 
banned when Steam updated a DLL file on disk after it had been loaded into memory by the 
game, causing a false positive detection. These bans were revoked and those affected received 
a free copy of Left 4 Dead 2 or an extra copy to send as a gift. 

- In October 2023, certain users of AMD graphics cards were banned from Counter-Strike 2 
after AMD added support for their &quot;Anti-Lag+&quot; feature via a driver update, which the game 
flagged as a cheat due to it detouring certain DLL functions. AMD subsequently withdrew the 
driver update and Valve pledged to unban any affected users.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This post isn’t created to bash Valve they clean up after their mistakes and listen to their community, 
gotta love devs when they do that. I also commend them because getting &lt;strong&gt;VAC&lt;/strong&gt; banned isn’t such a slap 
on the wrist. Getting &lt;strong&gt;VAC&lt;/strong&gt; banned has some stipulations such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Having the &lt;strong&gt;VAC&lt;/strong&gt; ban show on your Steam profile&lt;/li&gt;
  &lt;li&gt;Being banned from all &lt;strong&gt;“GoldSrc”&lt;/strong&gt; games&lt;/li&gt;
  &lt;li&gt;Being banned from all &lt;strong&gt;“Source engine”&lt;/strong&gt; games (The Counter-Strike serise)&lt;/li&gt;
  &lt;li&gt;Not being able to &lt;strong&gt;refund&lt;/strong&gt; the game you’re &lt;strong&gt;VAC&lt;/strong&gt; banned on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Knowing what’ll happen if you get &lt;strong&gt;VAC&lt;/strong&gt; banned is important to know because regardless if 
you’re cheating or not false bans are no good. People in the community took it upon themselves
to reverse engineer the Anti-Cheat and understand what it does (some did it just to cheat).&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;vac-what&quot;&gt;VAC what?&lt;/h1&gt;

&lt;p&gt;The previous section brings us to a term you may have heard before, the infamous &lt;strong&gt;“VAC Bypass”&lt;/strong&gt;.
Searching online for information about bypassing &lt;strong&gt;VAC&lt;/strong&gt; brings many blogs and repos that all seem to do/talk about something
similar and that’s “Dumping the &lt;strong&gt;VAC&lt;/strong&gt; modules”. Let me explain, &lt;strong&gt;VAC&lt;/strong&gt; is &lt;strong&gt;NOT&lt;/strong&gt; just one executable on the system it streams it’s 
Anti-Cheat modules (DLLs) from a server, once a module is recieved by some routine in &lt;strong&gt;steamservice.dll&lt;/strong&gt; inside &lt;strong&gt;steamservice.exe&lt;/strong&gt; 
(or &lt;strong&gt;steam.exe&lt;/strong&gt; if &lt;strong&gt;ran as admin&lt;/strong&gt;) it will be loaded using one of the two methods below. &lt;sup id=&quot;fnref:6&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; &lt;sup id=&quot;fnref:7&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; &lt;sup id=&quot;fnref:8&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; &lt;sup id=&quot;fnref:9&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:9&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; &lt;sup id=&quot;fnref:10&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; &lt;sup id=&quot;fnref:11&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:11&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;11&lt;/a&gt;&lt;/sup&gt; &lt;sup id=&quot;fnref:12&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:12&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;12&lt;/a&gt;&lt;/sup&gt; &lt;sup id=&quot;fnref:13&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:13&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Reflectively load&lt;/strong&gt; the DLL into memory&lt;/li&gt;
  &lt;li&gt;Use the WinAPI function &lt;strong&gt;LoadLibrary&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default the Anti-Cheat modules are reflectively loaded into memory. The goal is to force &lt;strong&gt;LoadLibrary&lt;/strong&gt; into being used, 
then someone can hook that function and dump the modules (DLLs) to disk, allowing someone to analyse the dumped DLLs and understand 
what they’re doing to detect cheating.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;dumping-vac-modules-in-the-big-25&quot;&gt;Dumping VAC Modules in the big ‘25&lt;/h1&gt;

&lt;p&gt;To kick start the journey on dumping the &lt;strong&gt;VAC&lt;/strong&gt; modules load &lt;strong&gt;steamservice.dll&lt;/strong&gt; into &lt;strong&gt;Binary Ninja&lt;/strong&gt;. Once the binary is fully analyzed
go to “Triage Summary”&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-1.png&quot; alt=&quot;VAC-1&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is &lt;strong&gt;VERY&lt;/strong&gt; important to take note that this is a 32-bit process, so all pointers will be 32-bits in size you’ll see why this is important later.&lt;/p&gt;

&lt;p&gt;Next we’ll search for calls to &lt;strong&gt;LoadLibrary*&lt;/strong&gt; I’ll save the reader some time and tell you that we should be looking
for calls to &lt;strong&gt;LoadLibraryW&lt;/strong&gt; it will be called in a very important function that we can use to back track.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-2.png&quot; alt=&quot;VAC-2&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Following the reference takes us to an interesting function &lt;strong&gt;sub_10086f80&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-3.png&quot; alt=&quot;VAC-3&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Judging by the return value &lt;strong&gt;HMODULE&lt;/strong&gt; and the calls to LoadLibrary* it’s safe to say this function’s job is to load
some kind of module and return a handle to it. Following references to where this function is called leads us to another interesting
function &lt;strong&gt;sub_10086c40&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-4.png&quot; alt=&quot;VAC-4&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The beginning of the &lt;strong&gt;sub_10086c40&lt;/strong&gt; function didn’t look too important (at the time) but we should remember that this function also returns a handle to a module.
I looked at the references and it shows that this function is called once in the function &lt;strong&gt;sub_10059040&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-17.png&quot; alt=&quot;VAC-17&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can see &lt;strong&gt;sub_10086c40&lt;/strong&gt; being called if we trace back the &lt;strong&gt;first&lt;/strong&gt; argument passed to that function,
we’ll see that it was used by another function &lt;strong&gt;sub_100859d0&lt;/strong&gt;. If that function call is successful 
execution carries on, so it’s safe to say it’s important. Let’s take a look at this function.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-18.png&quot; alt=&quot;VAC-5&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This function makes two WinAPI calls&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;dl&gt;
      &lt;dt&gt;&lt;strong&gt;GetTempPathW&lt;/strong&gt;&lt;/dt&gt;
      &lt;dd&gt;Retrieves the path of the directory designated for temporary files.&lt;/dd&gt;
    &lt;/dl&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;dl&gt;
      &lt;dt&gt;&lt;strong&gt;GetTempFileNameW&lt;/strong&gt;&lt;/dt&gt;
      &lt;dd&gt;Creates a name for a temporary file. If a unique file name is generated, an empty file is created and the handle to it is released; otherwise, only a file name is generated.&lt;/dd&gt;
    &lt;/dl&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The combination of these calls tells us that we need to be looking for any &lt;code&gt;.TMP&lt;/code&gt; files being accessed, the names are usually in this format &lt;code&gt;&amp;lt;uuuu&amp;gt;.TMP&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now there’s a path to a DLL floating in memory how does it get used? Look no more.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-5.png&quot; alt=&quot;VAC-5&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;100591f7   HMODULE eax_13 = sub_10086c40(edi_1, 0)
100591ff   *(esi + 4) = eax_13
100591ff   
10059204   if (eax_13 != 0)
10059215       int32_t eax_14 = sub_10086c20(eax_13, &quot;_runfunc@20&quot;)
1005921d       *(esi + 0xc) = eax_14
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The path &lt;code&gt;edi_1&lt;/code&gt; is used by &lt;strong&gt;sub_10086c40&lt;/strong&gt;, this function is used to get a handle to a module &lt;code&gt;eax_13&lt;/code&gt; then it’s passing that handle to &lt;strong&gt;sub_10086c20&lt;/strong&gt;.
&lt;strong&gt;sub_10086c20&lt;/strong&gt; takes two arguments we know the first is a handle to a module the second is from what we can see here a string &lt;code&gt;_runfunc@20&lt;/code&gt;, the return value
&lt;code&gt;int32_t&lt;/code&gt; looks a little weird but this is a 32-bit process remember ;) so this could be a pointer to something dont ya think? Here’s the function prototype&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;int32_t sub_10086c20(HMODULE arg1, PSTR arg2)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Place your bets on it being a GetProcAddress wrapper.. Drum roll please… It is…&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-6.png&quot; alt=&quot;VAC-6&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So with this bit of information we know &lt;strong&gt;steamservice.dll&lt;/strong&gt; recieves the &lt;strong&gt;VAC&lt;/strong&gt; modules, it’s using a function &lt;strong&gt;sub_10086c40&lt;/strong&gt; which calls
&lt;strong&gt;sub_10086f80&lt;/strong&gt; to load the Anti-Cheat module and return a handle, then that handle is passed to &lt;strong&gt;sub_10086c20&lt;/strong&gt; to get the address 
of a function named &lt;code&gt;_runfunc@20&lt;/code&gt;. By default as said earlier the modules are reflectively loaded so this isn’t the regular control flow of 
&lt;strong&gt;steamservice.dll&lt;/strong&gt;, this can be confirmed if you scroll up a bit in &lt;strong&gt;sub_10059040&lt;/strong&gt; you’ll see a flag being checked.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-7.png&quot; alt=&quot;VAC-7&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;steamservice.dll&lt;/strong&gt; will most likely take this path unless we can do something about it&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-8.png&quot; alt=&quot;VAC-8&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s look at it in assembly&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-9.png&quot; alt=&quot;VAC-9&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Take a look at &lt;code&gt;je 0x10059127&lt;/code&gt; ( &lt;code&gt;0x74 0x47&lt;/code&gt; )&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0x74&lt;/code&gt; is the jump if equal instruction and &lt;code&gt;0x47&lt;/code&gt; is how many bytes forward to jump (71) in hex&lt;/p&gt;

&lt;p&gt;What we wan’t to do is change the first instruction at &lt;strong&gt;steamservice.dll&lt;/strong&gt; + 0x590DE (0x100590de)&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;to &lt;code&gt;jne 0x10059127&lt;/code&gt; ( &lt;code&gt;0x75 0x47&lt;/code&gt; )&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;we’re changing the first byte of this instruction to be &lt;code&gt;0x75&lt;/code&gt; which is jump if &lt;strong&gt;NOT&lt;/strong&gt;
zero/equal. (Inverting)&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-10.png&quot; alt=&quot;VAC-10&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have a potential way of dumping the &lt;strong&gt;VAC&lt;/strong&gt; modules let’s test it! first we start steam and launch &lt;strong&gt;x32dbg&lt;/strong&gt; as
admin we should remember the offset to our instructions &lt;strong&gt;steamservice.dll&lt;/strong&gt; + 0x590DE.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-11.png&quot; alt=&quot;VAC-11&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once &lt;strong&gt;x32dbg&lt;/strong&gt; is loaded attach to &lt;strong&gt;steamservice.dll&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-12.png&quot; alt=&quot;VAC-12&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Press &lt;code&gt;CTRL+G&lt;/code&gt; and enter &lt;code&gt;steamservice.dll + 0x590DE&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-13.png&quot; alt=&quot;VAC-13&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we’re where we need to patch&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-14.png&quot; alt=&quot;VAC-14&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Right click on that instruction and click “Assemble” then enter &lt;code&gt;jnz 0x10059127&lt;/code&gt; and hit ok&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-15.png&quot; alt=&quot;VAC-15&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should be changed&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-16.png&quot; alt=&quot;VAC-16&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next step is to open &lt;strong&gt;Procmon&lt;/strong&gt; play a game that uses &lt;strong&gt;VAC&lt;/strong&gt; (I chose CSGO) and wait for
&lt;strong&gt;steamservice.exe&lt;/strong&gt; to access some &lt;code&gt;.TMP&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;Here are the &lt;strong&gt;Procmon&lt;/strong&gt; filters&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-19.png&quot; alt=&quot;VAC-19&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While loading the game we see our first TMP file &lt;code&gt;C:\Windows\Temp\D54A.tmp&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-20.png&quot; alt=&quot;VAC-20&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s join a public match and see if there are others&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-21.png&quot; alt=&quot;VAC-21&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And some more..&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-22.png&quot; alt=&quot;VAC-22&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can also look at these files in the temp directory..&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-23.png&quot; alt=&quot;VAC-23&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I copied all of these files to a new directory and loaded &lt;code&gt;D54A.tmp&lt;/code&gt; into &lt;strong&gt;PE-bear&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH2/VAC-24.png&quot; alt=&quot;VAC-24&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We see something familiar &lt;code&gt;_runfunc@20&lt;/code&gt; this is the function that was found using &lt;strong&gt;sub_10086c20&lt;/strong&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;to-be-continued&quot;&gt;To be continued&lt;/h1&gt;

&lt;p&gt;In the next post we will be doing analysis on these Anti-Cheat Modules to get a better understanding of what’s going on.
I hope you enjoyed this post and most importantly learned a thing or two. Stay tuned!&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;references&quot;&gt;References&lt;/h1&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://en.wikibooks.org/wiki/Windows_Programming/User_Mode_vs_Kernel_Mode&quot;&gt;User_Mode_vs_Kernel_Mode&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Valve_Anti-Cheat#Additional_restrictions&quot;&gt;List of games that use VAC - 1&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://areweanticheatyet.com/?search=VAC&amp;amp;sortOrder=&amp;amp;sortBy=&quot;&gt;List of games that use VAC - 2&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.engadget.com/2010-07-27-valve-apologizes-for-banning-over-12-000-legit-modern-warfare-2.html&quot;&gt;valve-apologizes-for-banning-over-12-000-legit-modern-warfare-2&lt;/a&gt; &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.tomshardware.com/news/amd-pulls-drivers-that-cause-counter-strike-2-bans-after-valve-roasted-them&quot;&gt;amd-pulls-drivers-that-cause-counter-strike-2-bans&lt;/a&gt; &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:6&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/danielkrupinski/VAC-Bypass&quot;&gt;danielkrupinski - VAC-Bypass&lt;/a&gt; &lt;a href=&quot;#fnref:6&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:7&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/nevioo1337/VAC-ModuleDumper/&quot;&gt;nevioo1337 - VAC-ModuleDumper&lt;/a&gt; &lt;a href=&quot;#fnref:7&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:8&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://whereisr0da.github.io/blog/posts/2021-03-10-quick-vac/&quot;&gt;whereisr0da - Valve Anti Cheat - Part 1 : Module loading&lt;/a&gt; &lt;a href=&quot;#fnref:8&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:9&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://cra0.net/blog/posts/archived/2015/rel-dumping-vac2-and-vac3-the-easier-way/&quot;&gt;Cra0 - Dumping VAC2 and VAC3 the easier way&lt;/a&gt; &lt;a href=&quot;#fnref:9&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:10&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/twokilohertz/VacDumperInternal&quot;&gt;twokilohertz -  VacDumperInternal&lt;/a&gt; &lt;a href=&quot;#fnref:10&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:11&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20240909100849/https://absceptual.me/posts/vac/&quot;&gt;absceptual.me - Reversing VAC: Initalization&lt;/a&gt; &lt;a href=&quot;#fnref:11&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:12&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/danielkrupinski/vac-hooks/blob/master/vac-hooks/dllmain.c#L7&quot;&gt;The actual patch we do in this blog&lt;/a&gt; &lt;a href=&quot;#fnref:12&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:13&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.unknowncheats.me/forum/anti-cheat-bypass/578728-preventvac-information.html&quot;&gt;noobesgt - PreventVAC + Information&lt;/a&gt; &lt;a href=&quot;#fnref:13&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Sun, 20 Apr 2025 00:00:00 +0000</pubDate>
        <link>https://codeneverdies.github.io/2025/04/20/GH2/</link>
        <guid isPermaLink="true">https://codeneverdies.github.io/2025/04/20/GH2/</guid>
      </item>
    
      <item>
        <title>Game Hacking: BakkesMod</title>
        <description>&lt;p&gt;So i’ve been playing rocket league for as long as I can remember, and I have this mod called
&lt;strong&gt;BakkesMod&lt;/strong&gt;. I have &lt;strong&gt;BakkesMod&lt;/strong&gt; to use skins I don’t have unlocked, you can basically call it a 
“skin changer” with one caveat, you can only see the skins client side. On the other hand it has 
loads of functionality.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-1.png&quot; alt=&quot;BM-1&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was only a matter of time before I thought to myself “How does this even work?” so I did what anyone else
in the world would do, and that was go to their &lt;a href=&quot;https://github.com/bakkesmodorg&quot;&gt;github repo&lt;/a&gt; :).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;the-injectahhhhh&quot;&gt;The Injectahhhhh&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;Upon visiting their profile I see many interesting repos but one catches my eye.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;https://github.com/bakkesmodorg/BakkesModInjectorCpp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the code for their DLL Injector, i.e place an arbitrary DLL inside the address space of another (remote) process. 
Once that DLL is inside of the remote process it creates a remote thread to call DllMain of the DLL that was injected, 
once DllMain is called execution starts and BakkesMod can do it’s thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DLL Injection&lt;/strong&gt; is not a new technique, it’s a widely known technique to get code to run inside of another
process. Sometimes malware and cheats use &lt;strong&gt;DLL Injection&lt;/strong&gt; to setup for things like hooking because once their DLL is loaded
into the target process it has access to the memory of that target process as well, meaning it can manipulate
the functionality of the process while it’s running which is a big win for someone who wants to do so.&lt;/p&gt;

&lt;p&gt;Sidenote: (&lt;strong&gt;BakkesMod&lt;/strong&gt; is considered a “Mod” not a “Cheat” hence the name, it does not give an unfair advantage)&lt;/p&gt;

&lt;p&gt;Here’s a code snippet from &lt;a href=&quot;https://www.ired.team/offensive-security/code-injection-process-injection/dll-injection#references&quot;&gt;ired-team&lt;/a&gt; demonstrating &lt;strong&gt;DLL Injection&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int main(int argc, char *argv[]) {
	HANDLE processHandle;
	PVOID remoteBuffer;
	wchar_t dllPath[] = TEXT(&quot;C:\\experiments\\evilm64.dll&quot;);
	
	printf(&quot;Injecting DLL to PID: %i\n&quot;, atoi(argv[1]));
	processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
	remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof dllPath, MEM_COMMIT, PAGE_READWRITE);	
	WriteProcessMemory(processHandle, remoteBuffer, (LPVOID)dllPath, sizeof dllPath, NULL);
	PTHREAD_START_ROUTINE threatStartRoutineAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT(&quot;Kernel32&quot;)), &quot;LoadLibraryW&quot;);
	CreateRemoteThread(processHandle, NULL, 0, threatStartRoutineAddress, remoteBuffer, 0, NULL);
	CloseHandle(processHandle); 
	
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;

&lt;p&gt;Now that we know BakkesMod does &lt;strong&gt;DLL Injection&lt;/strong&gt; we should be looking for these function calls inside the code&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;OpenProcess&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;WriteProcessMemory&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;VirtualAllocEx&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;VirtualProtect&lt;/code&gt; (maybe)&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;CreateRemoteThread&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking around you’ll see a couple files but one should stick out like a sore thumb, &lt;a href=&quot;https://github.com/bakkesmodorg/BakkesModInjectorCpp/blob/master/BakkesModInjectorC%2B%2B/DllInjector.cpp&quot;&gt;&lt;code&gt;DllInjector.cpp&lt;/code&gt;&lt;/a&gt; In this file you’ll find a function called &lt;a href=&quot;https://github.com/bakkesmodorg/BakkesModInjectorCpp/blob/master/BakkesModInjectorC%2B%2B/DllInjector.cpp#L51&quot;&gt;&lt;code&gt;InjectDLL&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-2.png&quot; alt=&quot;BM-2&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This function takes in a &lt;code&gt;std::wstring&lt;/code&gt; &lt;code&gt;processName&lt;/code&gt; which would most likely be &lt;code&gt;RocketLeague.exe&lt;/code&gt; and a &lt;code&gt;std::filesystem::path&lt;/code&gt; &lt;code&gt;path&lt;/code&gt; which would most likely be the path to the DLL to be injected. Inside the function we can see it’s doing the same exact thing we saw from &lt;code&gt;ired-team&lt;/code&gt;’s demonstration, 
the order of the functions are a little different but they both serve the same purpose and that’s Injecting a DLL into a remote process.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;On lines &lt;a href=&quot;https://github.com/bakkesmodorg/BakkesModInjectorCpp/blob/master/BakkesModInjectorC%2B%2B/DllInjector.cpp#L57&quot;&gt;&lt;code&gt;57-60&lt;/code&gt;&lt;/a&gt;
after it has a valid handle to the process it locates the address of &lt;code&gt;LoadLibraryW&lt;/code&gt; using &lt;code&gt;GetProcAddress&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-3.png&quot; alt=&quot;BM-3&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is also done in &lt;code&gt;ired-team&lt;/code&gt;’s demonstration here&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;PTHREAD_START_ROUTINE threatStartRoutineAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT(&quot;Kernel32&quot;)), &quot;LoadLibraryW&quot;);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Lines &lt;a href=&quot;https://github.com/bakkesmodorg/BakkesModInjectorCpp/blob/master/BakkesModInjectorC%2B%2B/DllInjector.cpp#L61&quot;&gt;&lt;code&gt;61-64&lt;/code&gt;&lt;/a&gt; 
handle allocating memory for the library name string and writing that to the allocation.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-4.png&quot; alt=&quot;BM-4&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/bakkesmodorg/BakkesModInjectorCpp/blob/master/BakkesModInjectorC%2B%2B/DllInjector.cpp#L65&quot;&gt;&lt;code&gt;65-77&lt;/code&gt;&lt;/a&gt; is where &lt;code&gt;CreateRemoteThread&lt;/code&gt; is called to start execution, after execution has started &lt;strong&gt;BakkesMod&lt;/strong&gt; waits and then does some cleanup.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-5.png&quot; alt=&quot;BM-5&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;looking at the &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread&quot;&gt;&lt;code&gt;CreateRemoteThread&lt;/code&gt;&lt;/a&gt; 
and &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw&quot;&gt;&lt;code&gt;LoadLibraryW&lt;/code&gt;&lt;/a&gt; 
documentation on msdn helps understand what’s going on.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;HMODULE LoadLibraryW(
  [in] LPCWSTR lpLibFileName
);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;LoadLibraryW&lt;/code&gt; takes in one parameter, a pointer to a widechar string
that is the name of a DLL to be loaded.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;HANDLE CreateRemoteThread(
  [in]  HANDLE                 hProcess,
  [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  [in]  SIZE_T                 dwStackSize,
  [in]  LPTHREAD_START_ROUTINE lpStartAddress,
  [in]  LPVOID                 lpParameter,
  [in]  DWORD                  dwCreationFlags,
  [out] LPDWORD                lpThreadId
);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In our case &lt;code&gt;lpStartAddress&lt;/code&gt; is the address of &lt;code&gt;LoadLibraryW&lt;/code&gt; and &lt;code&gt;lpParameter&lt;/code&gt; is &lt;code&gt;dereercomp&lt;/code&gt; which 
is a pointer to the name of the DLL to be loaded. So a thread will be created and once it starts &lt;code&gt;LoadLibraryW&lt;/code&gt;
will execute with whatever string is held at the memory of &lt;code&gt;dereercomp&lt;/code&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;gettin-dirty&quot;&gt;Gettin’ dirty&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;With all this information idk about you but this makes me wan’t to write my own DLL Injector, let’s get to it.
First we’ll start with finding the process ID of rocket league, this is a special number the OS uses to identify a process.
We need it to tell &lt;code&gt;OpenProcess&lt;/code&gt; what process we wan’t a handle to.&lt;/p&gt;

&lt;p&gt;Here’s my code to find the process ID of rocket league.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;DWORD get_pid() {

    DWORD pid = 0;
    PROCESSENTRY32 proc_entry = { .dwSize = sizeof(PROCESSENTRY32) };
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if ( Process32First(snapshot, &amp;amp;proc_entry) ) {
        do {
            if ( strcmp(proc_entry.szExeFile, &quot;RocketLeague.exe&quot;) == 0 ) {
                pid = proc_entry.th32ProcessID;
                break;
            }
        } while ( Process32Next(snapshot, &amp;amp;proc_entry) );
    }

    return pid;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once our process ID is found we can use &lt;code&gt;OpenProcess&lt;/code&gt; to get a handle&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;HANDLE rocket_league = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that we have a handle to the process we can allocate some memory for our widechar string&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;LPVOID allocated = VirtualAllocEx(rocket_league, NULL, path_sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next step is to write our DLL string to the allocated memory&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;WriteProcessMemory(rocket_league, allocated, path, path_sz, NULL);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Last but not least get the address of &lt;code&gt;LoadLibraryW&lt;/code&gt; using &lt;code&gt;GetProcAddress&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;
LPVOID loadlib = (LPVOID)GetProcAddress(GetModuleHandleW(L&quot;kernel32.dll&quot;), &quot;LoadLibraryW&quot;);

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and create a new thread inside rocket league pointing to &lt;code&gt;LoadLibraryW&lt;/code&gt; with one parameter &lt;code&gt;allocated&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;HANDLE thread = CreateRemoteThread(rocket_league, NULL, NULL, (LPTHREAD_START_ROUTINE)loadlib, allocated, 0, NULL);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I almost forgot to mention the dll we will be loading here it is.. Just a simple MessageBoxA payload&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;// loadme.c -&amp;gt; loadme.dll

#include &amp;lt;windows.h&amp;gt;

BOOL WINAPI DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) {

        switch (reason) {

                case DLL_PROCESS_ATTACH:
                        MessageBoxA(NULL, &quot;I was loaded&quot;, &quot;I was loaded&quot;, MB_OK);
                        break;

                case DLL_THREAD_ATTACH:
                        break;

                case DLL_THREAD_DETACH:
                        break;

                case DLL_PROCESS_DETACH:
                        break;

        }

        return TRUE;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With all of this let’s see how it looks under the hood.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;debug-time--͡-͜-&quot;&gt;Debug time ( ͡° ͜ °)&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;When our injector is open inside of x64dbg we set a breakpoint on “OpenProcess”&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-6.png&quot; alt=&quot;GH-6&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once we hit that break point and return, set a break point on VirtualAllocEx&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-7.png&quot; alt=&quot;BM-7&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before we return out of VirtualAllocEx look at the RAX register that will be
the return value, This is the address of our allocation inside of Rocket league.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-8.png&quot; alt=&quot;BM-8&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-9.png&quot; alt=&quot;BM-9&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now set a breakpoint on “WriteProcessMemory” execute that
and reread our memory region to see our payload&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-10.png&quot; alt=&quot;BM-10&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-11.png&quot; alt=&quot;BM-11&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Same as before set a breakpoint on “CreateRemoteThread” but look at the arguments passed
and remember the function prototype.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-12.png&quot; alt=&quot;BM-12&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
  &lt;li&gt;Is the process handle to the rocket league&lt;/li&gt;
  &lt;li&gt;the security attributes (NULL)&lt;/li&gt;
  &lt;li&gt;stack size (NULL)&lt;/li&gt;
  &lt;li&gt;lpStartAddress (address of LoadLibraryW)&lt;/li&gt;
  &lt;li&gt;lpParameter (first parameter to ^)&lt;/li&gt;
  &lt;li&gt;CreationFlags (0)&lt;/li&gt;
  &lt;li&gt;ThreadId output (NULL)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once this remote thread is created we see something nice&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/GH1/BM-FINAL.png&quot; alt=&quot;BM-FINAL&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;remarks&quot;&gt;Remarks&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;This post was aimed towards beginners/people who know little about game hacking/game security and want to learn more.
I am so fascinated by the idea that malware and cheats share the same techniques and I will continue to try
and document the things I find to support that idea. Thank you for reading.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread&quot;&gt;https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://pentestlab.blog/2017/04/04/dll-injection/&quot;&gt;https://pentestlab.blog/2017/04/04/dll-injection/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.ired.team/offensive-security/code-injection-process-injection/dll-injection#references&quot;&gt;ired-teams’s dllinjection&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/bakkesmodorg&quot;&gt;BakkesMod - Github repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/bakkesmodorg/BakkesModInjectorCpp&quot;&gt;Their injector repo&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 17 Apr 2025 00:00:00 +0000</pubDate>
        <link>https://codeneverdies.github.io/2025/04/17/GH1/</link>
        <guid isPermaLink="true">https://codeneverdies.github.io/2025/04/17/GH1/</guid>
      </item>
    
      <item>
        <title>Land Of The PEB: Running from the debugger</title>
        <description>&lt;p&gt;Welcome to the second installment of “Land Of The PEB”. Last post we talked about what the PEB was and some
ways it could be used to ones advantage. You can’t run from it trust me.. You can find it in your nearest neighborhood even the 
one you’re using to read this post (If you’re on windows), but one thing we may be able to run from is the debugger. Using 
the PEB for &lt;strong&gt;Anti-Debugging&lt;/strong&gt; purposes is a fairly known technique but don’t let that fool you it’s still a good thing to know.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;you-must-learn-to-walk-before-you-can-run&quot;&gt;“You must learn to walk before you can run”&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;So.. you wan’t to learn how to detect debuggers? There are a couple of ways to do so the two simplest ways I’ve found are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Check the &lt;strong&gt;BeingDebugged&lt;/strong&gt; member in the PEB&lt;/li&gt;
  &lt;li&gt;Check &lt;strong&gt;NtGlobalFlag&lt;/strong&gt; in the PEB &lt;strong&gt;(To see if the process was created by a debugger)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;these two don’t do much justice compared to other &lt;strong&gt;Anti-Debugging&lt;/strong&gt; methods but they sure are robust. We will start with the second method first.
If you’ve followed my previous post you should already have some way of getting a pointer to the PEB so I will be going off that. I will be doing this in C/ASM.&lt;/p&gt;

&lt;p&gt;Here is my assembly code to get a pointer to the PEB &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;
get_peb:
    push rdi
    mov rdi, rsp
    sub rsp, 0x20
    xor rax, rax
    mov rax, gs:[0x60]
    mov rsp, rdi
    pop rdi
    ret
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;running-to-the-ntglobalflag&quot;&gt;Running to the (NtGlobal)Flag&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before we move on we should talk about what &lt;strong&gt;NtGlobalFlag&lt;/strong&gt; is and how we can get to it.&lt;/p&gt;

&lt;p&gt;In the windows registry we can see that the “GlobalFlag” key’s value is set to 0
This tells us that this is the default value.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/registry-globalflag.png&quot; alt=&quot;registry-globalflag&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This can be confirmed by starting any process like notepad.exe, attaching a debugger to it and looking
at the &lt;strong&gt;NtGlobalFlag&lt;/strong&gt; in the PEB. Notice how I said to start a process then attach, it will make much sense later.&lt;/p&gt;

&lt;p&gt;Once notepad is open click “attach” in x64dbg and find notepad.exe in the list of processes.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/x64dbg-attach.png&quot; alt=&quot;x64dbg-attach&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you’re in x64dbg hit run until notepad is fully functional. This should look familiar, we are going to
index &lt;code&gt;0x60&lt;/code&gt; bytes into the TEB (&lt;code&gt;gs&lt;/code&gt;) using this command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mov rax, gs:[0x60]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now the address of the PEB should be in the RAX register.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/PEB-RAX.png&quot; alt=&quot;PEB-RAX&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Can be confirmed if you right click on that address in the RAX register and click “Follow in Memory Map”&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/MMAP-PEB.png&quot; alt=&quot;MMAP-PEB&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With this pointer we can grab the &lt;strong&gt;NtGlobalFlag&lt;/strong&gt; value in the PEB which is at offset
&lt;code&gt;0xBC&lt;/code&gt; on 64-bit systems with this command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mov al, [rax+0xBC]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you followed correctly earlier, the value in the RAX register should NOT change due to the fact
that the notepad process was not created by a debugger we only started notepad and then attached the debugger to it.&lt;/p&gt;

&lt;p&gt;Now we will write a small program to check this flag and we will start it with a debugger. &lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;    ... snip ...
    
    xor rax, rax
    mov rax, gs:[0x60]
    
    mov al, [rax+0xBC]
    and al, 0x70
    cmp al, 0x70
    jz goodbye
    xor rax, rax

    ... snip ...
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;goodbye:
    xor rax, rax
    add al, 1
    mov rsp, rdi
    pop rdi
    ret
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once the binary is loaded into x64dbg we can see our function&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/Func.png&quot; alt=&quot;Func&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Right before we execute the instruction &lt;code&gt;mov al,byte ptr ds:[rax+BC]&lt;/code&gt; we can see again that a pointer to the PEB
is in the RAX.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/RAX-PEB2.png&quot; alt=&quot;RAX-PEB2&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After that instruction is executed we can see that the &lt;code&gt;al&lt;/code&gt; register changed which is the lowest byte of the RAX register
it is now &lt;code&gt;0x70&lt;/code&gt; why?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/FLAGS-SET.png&quot; alt=&quot;FLAGS-SET&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is because when a process is created by a debugger three flags will be set:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;FLG_HEAP_ENABLE_TAIL_CHECK&lt;/strong&gt; = 0x10&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FLG_HEAP_ENABLE_FREE_CHECK&lt;/strong&gt; = 0x20&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FLG_HEAP_VALIDATE_PARAMETERS&lt;/strong&gt; = 0x40&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s a good idea to check against these flags explicitly because just checking if &lt;strong&gt;NtGlobalFlag&lt;/strong&gt;
is not 0 shouldn’t mean there is a debugger running. In short we should only check for a combination of
ALL of these flags &lt;code&gt;0x70&lt;/code&gt; to prevent false positives.&lt;/p&gt;

&lt;p&gt;Once that value is saved into the &lt;code&gt;al&lt;/code&gt; register we do a bitwise AND on &lt;code&gt;0x70&lt;/code&gt; and &lt;code&gt;al&lt;/code&gt;, then
we compare &lt;code&gt;al&lt;/code&gt; and &lt;code&gt;0x70&lt;/code&gt; to check if the combination of flags are set. We can see that
after we execute &lt;code&gt;cmp al, 0x70&lt;/code&gt; the Zero flag (ZF) gets set (if the result of an operation
is zero the Zero flag gets set to 1) this means that the value of &lt;strong&gt;NtGlobalFlag&lt;/strong&gt;, was equal to &lt;code&gt;0x70&lt;/code&gt;. 
that most likely means this process was created by a debugger (we know it was).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/ZERO-FLAG.png&quot; alt=&quot;ZERO-FLAG&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;i-hate-being-debugged&quot;&gt;I hate Being (De)bugged!!&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;The good ol’ &lt;strong&gt;BeingDebugged&lt;/strong&gt; if the last technique was too much for you, this is one of the most 
trivial &lt;strong&gt;Anti-Debugging&lt;/strong&gt; techniques out there. The &lt;strong&gt;BeingDebugged&lt;/strong&gt; member of the PEB is used to check if 
the current process is being debugged or not. We can be see it in WinDbg using two commands &lt;code&gt;r $peb&lt;/code&gt; -&amp;gt; &lt;code&gt;dt [address] ntdll!_peb&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/BD.png&quot; alt=&quot;BD&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can see that it’s located at offset &lt;code&gt;0x02&lt;/code&gt; and the value is set to &lt;code&gt;0x1&lt;/code&gt; (true). With this information
we can write a function to do this for us.&lt;/p&gt;

&lt;p&gt;I’m going to stick to ASM because I think it’s good practice, we know we must do two things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Get a pointer to the PEB&lt;/li&gt;
  &lt;li&gt;Get the &lt;strong&gt;BeingDebugged&lt;/strong&gt; value&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;    ... snip ...

    mov rax, gs:[0x60]  // You should know this by now
    mov al, [rax+0x02]  // Grabbing the BeingDebugged value
    and rax, 0x000000FF // Clearing out everything except for al
   
    ... snip ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Back to x64dbg! Again right after we execute the instruction &lt;code&gt;mov rax,qword ptr gs:[60]&lt;/code&gt; 
we can see that a pointer to the PEB is in the RAX. If we right click on this address in RAX
and select “Follow in Dump” we can see there’s an &lt;code&gt;0x1&lt;/code&gt; two bytes in&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/0x1-Dump.png&quot; alt=&quot;0x1-Dump&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stepping two more times does the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Moves the &lt;strong&gt;BeingDebugged&lt;/strong&gt; value to the &lt;code&gt;al&lt;/code&gt; register&lt;/li&gt;
  &lt;li&gt;Clears the remaning bits except the lowest 8&lt;/li&gt;
  &lt;li&gt;After these steps RAX should contain &lt;code&gt;0000000000000001&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/Clean-ret.png&quot; alt=&quot;Clean-ret&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;remarks&quot;&gt;Remarks&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those were two ways malware may detect if it’s being debugged there are numerous ways to do so. If you’re doing malware analysis
It should also be known that these two methods can easily be bypassed by simply changing the values to 0 in memory before they are accessed, like so.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/anti-debugging-bypass1.png&quot; alt=&quot;anti-debugging-bypass1&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Click “Ok” and the value will change&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/Changed.png&quot; alt=&quot;Changed&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;strong&gt;BeingDebugged&lt;/strong&gt; value was changed to &lt;code&gt;0x0&lt;/code&gt; (false)&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/images/LOTP2/Done.png&quot; alt=&quot;Done&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://www.aldeid.com/wiki/PEB-Process-Environment-Block/NtGlobalFlag&quot;&gt;aldeid.com - NtGlobalFlag&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ctf-wiki.mahaloz.re/reverse/windows/anti-debug/ntglobalflag/#manual-bypass-example&quot;&gt;mahaloz - NtGlobalFlag bypass&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/enable-heap-parameter-checking&quot;&gt;msdn - heap-parameter-checking&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/enable-heap-free-checking&quot;&gt;msdn - heap-free-checking&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/enable-heap-tail-checking&quot;&gt;msdn - heap-tail-checking&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://anti-debug.checkpoint.com/techniques/debug-flags.html#manual-checks-ntglobalflag&quot;&gt;checkpoint research &amp;lt;3 - NtGlobalFlag&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm&quot;&gt;geoffchappell - peb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://malgamy.github.io/revese%20enginnering/Anti-debugging-and-anti-tracing-techniques-part3/&quot;&gt;malgamy - Anti-debugging 0x03 &lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;https://github.com/codeneverdies/ws-loader/blob/main/asm/init.asm#L23 &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;https://github.com/codeneverdies/ws-loader/blob/main/asm/init.asm#L36 &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Wed, 26 Mar 2025 00:00:00 +0000</pubDate>
        <link>https://codeneverdies.github.io/2025/03/26/LOTP2/</link>
        <guid isPermaLink="true">https://codeneverdies.github.io/2025/03/26/LOTP2/</guid>
      </item>
    
      <item>
        <title>Land Of The PEB: Modules and DLLs</title>
        <description>&lt;p&gt;Welcome to my first series called “Land Of The PEB” where I will be discussing various topics
related to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Process_Environment_Block&quot;&gt;Process Environment Block&lt;/a&gt; (PEB).&lt;/p&gt;

&lt;h3 id=&quot;what-is-this-thing&quot;&gt;What is this thing?&lt;/h3&gt;

&lt;p&gt;The Process Environment Block (we will refer to as PEB) is a data structure in the &lt;a href=&quot;https://en.wikipedia.org/wiki/Windows_NT&quot;&gt;Windows NT&lt;/a&gt; 
operating system that holds lots of useful information about the current process like..&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Debug Flags&lt;/li&gt;
  &lt;li&gt;Process start command line arguments&lt;/li&gt;
  &lt;li&gt;Pointer to the process’s heap&lt;/li&gt;
  &lt;li&gt;The base address of the current process.&lt;/li&gt;
  &lt;li&gt;List of loaded modules (DLLs)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;how-can-i-use-it&quot;&gt;How can I use it?&lt;/h3&gt;

&lt;p&gt;In this post we will be focusing on the member that relates to loaded modules i.e, &lt;strong&gt;PEB_LDR_DATA&lt;/strong&gt;
and we will be using this structure to write our own version of &lt;strong&gt;GetModuleHandle&lt;/strong&gt;. To accomplish this
we must do the following..&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Get a pointer to the PEB&lt;/li&gt;
  &lt;li&gt;Get a pointer to &lt;strong&gt;PEB_LDR_DATA&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Get a pointer to the &lt;strong&gt;InMemoryOrderModuleList&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once we get to this list in memory we can loop through this list and compare each module name with
the one we want for example we will do “ntdll.dll” this could be useful if you don’t want &lt;strong&gt;GetModuleHandle&lt;/strong&gt;
in your Import Address Table.&lt;/p&gt;

&lt;h3 id=&quot;to-the-land-of-the-peb&quot;&gt;To the land of the PEB!&lt;/h3&gt;

&lt;p&gt;It must be known that the PEB is actually located inside of the Thread Environment Block (TEB) at offset &lt;code&gt;0x60&lt;/code&gt; on 64-bit systems
and &lt;code&gt;0x30&lt;/code&gt; on 32-bit systems. There are two ways I know of getting a pointer to the PEB they both access the TEB located in the &lt;code&gt;gs&lt;/code&gt; 
segment register. The first is very useful if you don’t wan’t to write assembly.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;//64-bit systems
PPEB peb = (PPEB)__readgsqword(0x60);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These are intrinsic functions that do something close to this.. &lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;    push rdi
    mov rdi, rsp
    sub rsp, 0x20
        
    xor rax, rax
    mov rax, gs:[0x60]

    mov rsp, rdi
    pop rdi
    ret
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that we have a pointer to the PEB lets look for our module, we can start with
getting a pointer to &lt;strong&gt;PPEB_LDR_DATA&lt;/strong&gt; &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;PPEB_LDR_DATA ldr = peb-&amp;gt;Ldr;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once we have both of those pointers we can get our last needed pointer which is to the
InMemoryOrderModuleList. This points to the first entry in the list, each entry is of type LIST_ENTRY
which means each entry has a forward link pointing to the next module in the list. For each entry
we will compare it’s name with “ntdll.dll” if the names match we have found ntdll.dll and we grab the base
address. &lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;
entry = &amp;amp;ldr-&amp;gt;InMemoryOrderModuleList;
next_entry = entry-&amp;gt;Flink;

for ( LIST_ENTRY *e = next_entry; e != entry; e = e-&amp;gt;Flink ) {

    data_tbl = (LDR_DATA_TABLE_ENTRY *)((BYTE *)e - sizeof(LIST_ENTRY));

    if ( wcscmp(data_tbl-&amp;gt;BaseDllName.pBuffer, &quot;ntdll.dll&quot;) == 0 ) {
        mod = (HMODULE)(data_tbl-&amp;gt;DllBase);
        break;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;https://github.com/codeneverdies/ws-loader/blob/main/asm/init.asm#L23 &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;https://github.com/codeneverdies/ws-loader/blob/main/src/util.c#L48 &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;https://github.com/codeneverdies/ws-loader/blob/main/src/util.c#L50 &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Wed, 26 Mar 2025 00:00:00 +0000</pubDate>
        <link>https://codeneverdies.github.io/2025/03/26/LOTP1/</link>
        <guid isPermaLink="true">https://codeneverdies.github.io/2025/03/26/LOTP1/</guid>
      </item>
    
  </channel>
</rss>
