Showing newest posts with label anti-anti-cheat. Show older posts
Showing newest posts with label anti-anti-cheat. Show older posts

Saturday, December 19, 2009

AIO Bot Driver

AIO Bot uses a driver (or so they say) for some of their Warden protection.

I'm on an x64 machine and I really cbf setting up a kernel debugger on my XP x86 VM, so for now a static analysis will have to suffice.

They seem to perform a single kernelmode hook, it is on ZwQuerySystemInformation. They use it to hide processes and adjust some processor information.

The former they use because their bot uses a process for its GUI. What they don't seem to have realized is:

  • Warden does NOT scan processes.
  • They got the process names wrong in their hook, so even if Warden was looking for them, their hook wouldn't work. (Lol)
  • There are information codes other than SystemProcessInformation which return the exact same list, but with just a tiny bit of extra information. All of these codes must be handled otherwise their hook can by bypassed. (Good luck finding all of them, most are 100% undocumented and will require reversing of the Windows kernel.)
The latter I have no idea about. I was hoping someone with some more kernelmode experience could shed some light on that.

Overall their driver seems to be very poorly written, and is obviously the work of a complete amateur. I mean, shit, I have very little kernel-mode experience and even I could do a better job than this clusterfuck.

Here is the full dump of their hook sub, cleaned up, named, and documented by Kynox and I.

NTSTATUS __stdcall ZwQuerySystemInformation_Hook(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength)
{
  PVOID pCurrentEntry; // ebx@1
  PVOID pPreviousEntry; // ebp@3
  unsigned int TempIdleTime1; // eax@7
  unsigned __int8 TempIdleTimeFlag1; // cf@7
  unsigned int TempKernelTime1; // eax@7
  unsigned __int8 TempKernelTimeFlag1; // cf@7
  unsigned __int8 TempIdleTimeFlag2; // cf@11
  unsigned __int8 TempKernelTimeFlag2; // cf@11
  int IdleTimeTemp2; // ecx@18
  unsigned __int8 IdleTimeTemp3; // cf@18
  NTSTATUS TrampResult; // [sp+1Ch] [bp+10h]@1

  pCurrentEntry = SystemInformation;
  // Call original API
  TrampResult = ZwQuerySystemInformationTramp(
                  SystemInformationClass,
                  SystemInformation,
                  SystemInformationLength,
                  ReturnLength);
  // Check if original API succeeded
  if ( TrampResult >= 0 )
  {
    // Check for SystemProcessInformation
    if ( SystemInformationClass == 5 )
    {
      pPreviousEntry = 0;
      if ( SystemInformation )
      {
        do
        {
          // Check pCurrent->ImageName.Buffer
          if ( *((_DWORD *)pCurrentEntry + 15) )
          {
            // Check for the presence of a couple of unknown processes.
            // This conditional will always fail because there are no processes used by these names!
            if ( !strncmp(*((const char **)pCurrentEntry + 15), (const char *)L"wow007", 12)
              || !strncmp(*((const char **)pCurrentEntry + 15), (const char *)L"wowmf", 10) )
            {
              // Do something with pCurrentEntry->UniqueProcessId
              sub_11151(*((_DWORD *)pCurrentEntry + 17));
              // Modify pCurrentEntry->IdleTime
              TempIdleTime1 = *((_DWORD *)pCurrentEntry + 10);
              TempIdleTimeFlag1 = TempIdleTime1 >= -(_DWORD)qword_120F8;
              LODWORD(qword_120F8) = TempIdleTime1 + qword_120F8;
              HIDWORD(qword_120F8) += *((_DWORD *)pCurrentEntry + 11) + TempIdleTimeFlag1;
              // Modify pCurrentEntry->KernelTime
              TempKernelTime1 = *((_DWORD *)pCurrentEntry + 12);
              TempKernelTimeFlag1 = TempKernelTime1 >= -(_DWORD)qword_120E8;
              LODWORD(qword_120E8) = TempKernelTime1 + qword_120E8;
              HIDWORD(qword_120E8) += *((_DWORD *)pCurrentEntry + 13) + TempKernelTimeFlag1;
              // Advance to next entry if there are still entries to process
              if ( pPreviousEntry )
              {
                if ( *(_DWORD *)pCurrentEntry )
                  *(_DWORD *)pPreviousEntry += *(_DWORD *)pCurrentEntry;
                else
                  *(_DWORD *)pPreviousEntry = 0;
              }
            }
          }
          else
          {
            // Modify pCurrentEntry->IdleTime and pCurrentEntry->KernelTime even on unnamed processes
            TempIdleTimeFlag2 = (_DWORD)qword_120F8 >= (unsigned int)-*((_DWORD *)pCurrentEntry + 10);
            *((_DWORD *)pCurrentEntry + 10) += qword_120F8;
            *((_DWORD *)pCurrentEntry + 11) += HIDWORD(qword_120F8) + TempIdleTimeFlag2;
            TempKernelTimeFlag2 = (_DWORD)qword_120E8 >= (unsigned int)-*((_DWORD *)pCurrentEntry + 12);
            *((_DWORD *)pCurrentEntry + 12) += qword_120E8;
            *((_DWORD *)pCurrentEntry + 13) += HIDWORD(qword_120E8) + TempKernelTimeFlag2;
            qword_120E8 = 0i64;
            qword_120F8 = 0i64;
          }
          // Advance to next entry if there are still entries to process
          pPreviousEntry = pCurrentEntry;
          if ( *(_DWORD *)pCurrentEntry )
            pCurrentEntry = (char *)pCurrentEntry + *(_DWORD *)pCurrentEntry;
          else
            pCurrentEntry = 0;
        }
        while ( pCurrentEntry );
      }
    }
    else
    {
      // SystemProcessorPerformanceInformation
      if ( SystemInformationClass == 8 )
      {
        // Increase IdleTime
        IdleTimeTemp2 = (unsigned __int64)(qword_120F8 + qword_120E8) >> 32;
        IdleTimeTemp3 = (_DWORD)qword_120F8 + (_DWORD)qword_120E8 >= (unsigned int)-*(_DWORD *)SystemInformation;
        *(_DWORD *)SystemInformation += qword_120F8 + qword_120E8;
        *((_DWORD *)SystemInformation + 1) += IdleTimeTemp2 + IdleTimeTemp3;
      }
    }
  }
  return TrampResult;
}



Monday, November 23, 2009

WinNinja Module Hiding Code

Project:
WinNinja

Description:
Unlinking modules from the PEB is fairly common practice, however something which lots of people are unaware of is that this is only half of what you need to do to hide your module.

When the Windows PE loader maps a module into a process it uses a file mapping. The actual file mapping handle and other info is stored in the kernel, and cannot be hidden in the same way that the module loader data can be.

This opens a huge gaping hole that allows anyone to find your module in memory, even when it's unlinked.

This is done via calling NtQueryVirtualMemory with the MemorySectionName parameter on every memory region. This API will return the full path (kernel-style) to your module.

Furthermore, the fact that you can enumerate all memory regions in order to find potential module bases is in itself a weakness, as the attacker could perform hashing attacks (much like Warden does currently).

If you don't believe me, unlink your module from the PEB, download the VMMap tool from Sysinternals, and run it on the process your module is injected into. It will be able to retrieve the full path to your module, along with it's memory region.

So, in order to hide an injected DLL effectively from an anti-cheat system, you will have to hook NtQueryVirtualMemory, there is no way around of it (in usermode).

One problem is that NtQueryVirtualMemory allows inspection of the memory in an arbitrary process, so it's impossible to hide your DLL from an external process unless you do one of two things:

  • Hook the external process too
  • Manually map your module
Thankfully, the main reason you'd want to hide a module is to evade anti-cheat software, and in this case, we always know the process we need to hook, and we're not worried about some arbitrary process (like for example, VMMap).

Attached is an example implementation of such a hook.

Notes:
  • There are multiple known holes in it, and the documentation indicating where they are has been removed (I don't want to take away all the challenge, that would be no fun).
  • It is incomplete, in the sense that I've pulled it from part of a larger library of mine. You will need to provide the relevant structures, enumerations, functions, etc on your own. 
  • I'm using a C++0x compiler, if you're still working with C++03 or C++98 you will need to modify the code a big to get it to compile, however this is only a 30 second job.
  • Think of this more as a "pseudocode" example than a real implementation that you can just drop into your cheat.
Anyway, if you're not a moron this should provide a quick and dirty starting point for you.

As always, comments and constructive criticisms are appreciated. Also, if you notice any of the holes and want to help out the other readers you're free to post them in the comments. I just didn't want to post them myself because I thought it would be more fun to see how many you guys can find.

This may not appear anywhere else without permission, but may be linked to.

Releases:

Saturday, October 31, 2009

Hades Now Fully Dynamic

Hooray, I have now gotten all of Hades working in a fully dynamic manner. Previously in order to run a game under Hades you would have to inject Hades at the time of process creation. Recently though I have been rewriting large portions of Hades, and one of those portions has been the hooking lib I'm using.

Whilst my original hooking library used IAT and EAT hooks I found that far too restrictive for my needs, so I wrote an inline hooking library (similar to Microsoft Detours, except mine has full x64 support -- technically Microsoft Detours does too, but you have to pay $10000 to get your hands on it).

Not only does this mean I can now inject Hades at any time, it also means I can eject Hades at any time. This is AWESOME when it comes to testing because it means I can rapidly test new features and code without having to restart the game (which was a huge pain previously).

It also means I have full support for the Steam overlay (which was previously flaky because it doesn't like it when it's not the first module to hook), and I can implement some of my planned features much easier.

One of the next planned features I want to tackle (after some more rewriting of the core framework) is DirectX 10 support, which I've been putting off for a long time because previously I would have been forced to do a full device replacement like I did with the DirectX 9 layer.

The new hooking library though has allowed me to turn about 4 pages of code for the DX9 layer into about half a page, and the same can be applied to the DX10 layer.

Whilst it's not ready for public consumption yet, I'm considering releasing my hooking library if there's enough interest for it.

It may be a while though because before I get the hooking library ready for public use there's a lot more other stuff I want to work on in Hades like the Lua API, the .NET API, the generic anti-anti-cheat system, and better DirectInput support (which is currently quite basic due primarily to a lack of games I own which utilize it heavily).

Anyway, someone requested information on Hades from me on MSN and told me to post it here so there you go. Should be interesting to someone.