Showing newest posts with label anti-cheat. Show older posts
Showing newest posts with label 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;
}



AIO Bot

The AIO Bot thread on MMOwned is about to be deleted, so I'm recording here what happened for posterities sake (and to expose it to anyone who does not read MMOwned).

Basically, AIO Bot is a new bot that was advertised on MMOwned. They made quite a few bold claims that Kynox and myself decided to verify. Turns out they were lying.

Kynox has also done a write up on his blog, available here.

This bot has zero Warden protection, and it is in fact WORSE than WoWMimic in its current form. Its pretty much a clone of WoWMimic v1.

Proof of their module being injected:


Proof of their hooks:
Microsoft (R) Windows Debugger Version 6.11.0001.404 X86
Copyright (c) Microsoft Corporation. All rights reserved.

*** wait with pending attach
Symbol search path is: srv*
Executable search path is:
ModLoad: 00400000 011cd000   C:\Users\Public\Games\World of Warcraft\WoW.exe
ModLoad: 77280000 77400000   C:\Windows\SysWOW64\ntdll.dll
ModLoad: 76d80000 76e80000   C:\Windows\syswow64\kernel32.dll
ModLoad: 75690000 756d6000   C:\Windows\syswow64\KERNELBASE.dll
ModLoad: 6b790000 6b858000   C:\Windows\system32\OPENGL32.dll
ModLoad: 75130000 751dc000   C:\Windows\syswow64\msvcrt.dll
ModLoad: 74f90000 75030000   C:\Windows\syswow64\ADVAPI32.dll
ModLoad: 769d0000 769e9000   C:\Windows\SysWOW64\sechost.dll
ModLoad: 76b90000 76c80000   C:\Windows\syswow64\RPCRT4.dll
ModLoad: 74df0000 74e50000   C:\Windows\syswow64\SspiCli.dll
ModLoad: 74de0000 74dec000   C:\Windows\syswow64\CRYPTBASE.dll
ModLoad: 75cd0000 75d60000   C:\Windows\syswow64\GDI32.dll
ModLoad: 754c0000 755c0000   C:\Windows\syswow64\USER32.dll
ModLoad: 75bd0000 75bda000   C:\Windows\syswow64\LPK.dll
ModLoad: 75b30000 75bcd000   C:\Windows\syswow64\USP10.dll
ModLoad: 6cfb0000 6cfd2000   C:\Windows\system32\GLU32.dll
ModLoad: 73830000 73917000   C:\Windows\system32\DDRAW.dll
ModLoad: 74150000 74156000   C:\Windows\system32\DCIMAN32.dll
ModLoad: 769f0000 76b8d000   C:\Windows\syswow64\SETUPAPI.dll
ModLoad: 75400000 75427000   C:\Windows\syswow64\CFGMGR32.dll
ModLoad: 75430000 754bf000   C:\Windows\syswow64\OLEAUT32.dll
ModLoad: 751e0000 7533c000   C:\Windows\syswow64\ole32.dll
ModLoad: 75d60000 75d72000   C:\Windows\syswow64\DEVOBJ.dll
ModLoad: 71d60000 71d73000   C:\Windows\system32\dwmapi.dll
ModLoad: 725d0000 725d9000   C:\Windows\system32\VERSION.dll
ModLoad: 75c70000 75cd0000   C:\Windows\syswow64\IMM32.dll
ModLoad: 755c0000 7568c000   C:\Windows\syswow64\MSCTF.dll
ModLoad: 76c80000 76d74000   C:\Windows\syswow64\WININET.dll
ModLoad: 750c0000 75117000   C:\Windows\syswow64\SHLWAPI.dll
ModLoad: 75120000 75123000   C:\Windows\syswow64\Normaliz.dll
ModLoad: 74e50000 74f85000   C:\Windows\syswow64\urlmon.dll
ModLoad: 75a10000 75b2c000   C:\Windows\syswow64\CRYPT32.dll
ModLoad: 77250000 7725c000   C:\Windows\syswow64\MSASN1.dll
ModLoad: 756e0000 758d9000   C:\Windows\syswow64\iertutil.dll
ModLoad: 75940000 75975000   C:\Windows\syswow64\WS2_32.dll
ModLoad: 75980000 75986000   C:\Windows\syswow64\NSI.dll
ModLoad: 6d100000 6d130000   C:\Windows\system32\DINPUT8.dll
ModLoad: 75d80000 769c9000   C:\Windows\syswow64\SHELL32.dll
ModLoad: 10000000 10069000   C:\Users\Public\Games\World of Warcraft\DivxDecoder.dll
ModLoad: 72ed0000 72f02000   C:\Windows\system32\WINMM.dll
ModLoad: 74900000 74914000   C:\Windows\system32\MSACM32.dll
ModLoad: 73c90000 73c99000   C:\Windows\system32\HID.DLL
ModLoad: 720b0000 720fb000   C:\Windows\system32\apphelp.dll
ModLoad: 6d080000 6d0fb000   C:\Windows\AppPatch\AcSpecfc.DLL
ModLoad: 75030000 750b4000   C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.7600.16385_none_ebf82fc36c758ad5\COMCTL32.dll
ModLoad: 73c00000 73c79000   C:\Windows\system32\mscms.dll
ModLoad: 72f10000 72f27000   C:\Windows\system32\USERENV.dll
ModLoad: 74b10000 74b1b000   C:\Windows\system32\profapi.dll
ModLoad: 72410000 72422000   C:\Windows\system32\MPR.dll
ModLoad: 75990000 75a0b000   C:\Windows\syswow64\COMDLG32.dll
ModLoad: 59f60000 5a1a0000   C:\Windows\system32\msi.dll
ModLoad: 72020000 720ac000   C:\Windows\AppPatch\AcLayers.DLL
ModLoad: 72530000 72581000   C:\Windows\system32\WINSPOOL.DRV
ModLoad: 72d90000 72db1000   C:\Windows\system32\ntmarta.dll
ModLoad: 758f0000 75935000   C:\Windows\syswow64\WLDAP32.dll
ModLoad: 71fa0000 72020000   C:\Windows\system32\uxtheme.dll
ModLoad: 655f0000 657b3000   C:\Windows\system32\d3d9.dll
ModLoad: 73be0000 73be6000   C:\Windows\system32\d3d8thk.dll
ModLoad: 6f0d0000 6f9c5000   C:\Windows\system32\nvd3dum.dll
ModLoad: 002f0000 002fc000   C:\Program Files (x86)\Ad Muncher\AM31318.dll
ModLoad: 041f0000 0432b000   C:\Windows\system32\nvapi.dll
ModLoad: 01260000 01288000   C:\Program Files (x86)\NVIDIA Corporation\3D Vision\nvStereoApiI.dll
ModLoad: 03100000 03153000   C:\Program Files (x86)\NVIDIA Corporation\3D Vision\nvSCPAPI.dll
ModLoad: 75340000 7536d000   C:\Windows\syswow64\WINTRUST.dll
ModLoad: 74840000 74865000   C:\Windows\system32\powrprof.dll
ModLoad: 75be0000 75c63000   C:\Windows\syswow64\CLBCatQ.DLL
ModLoad: 74b80000 74bb9000   C:\Windows\System32\MMDevApi.dll
ModLoad: 74990000 74a85000   C:\Windows\System32\PROPSYS.dll
ModLoad: 74930000 74966000   C:\Windows\system32\AUDIOSES.DLL
ModLoad: 74b50000 74b80000   C:\Windows\system32\wdmaud.drv
ModLoad: 74980000 74984000   C:\Windows\system32\ksuser.dll
ModLoad: 74970000 74977000   C:\Windows\system32\AVRT.dll
ModLoad: 74920000 74928000   C:\Windows\system32\msacm32.drv
ModLoad: 748f0000 748f7000   C:\Windows\system32\midimap.dll
ModLoad: 71e00000 71f9e000   C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7600.16385_none_421189da2b7fabfc\comctl32.dll
ModLoad: 724a0000 724e4000   C:\Windows\system32\dnsapi.DLL
ModLoad: 72500000 7251c000   C:\Windows\system32\iphlpapi.DLL
ModLoad: 724f0000 724f7000   C:\Windows\system32\WINNSI.DLL
ModLoad: 73d70000 73d95000   C:\Windows\system32\peerdist.dll
ModLoad: 72cf0000 72d0b000   C:\Windows\system32\AUTHZ.dll
ModLoad: 0e780000 0e7c2000   C:\Windows\system32\nvLsp.dll
ModLoad: 758e0000 758e5000   C:\Windows\syswow64\PSAPI.DLL
ModLoad: 72600000 7263c000   C:\Windows\system32\mswsock.dll
ModLoad: 725e0000 725e5000   C:\Windows\System32\wshtcpip.dll
ModLoad: 74510000 74562000   C:\Windows\system32\RASAPI32.dll
ModLoad: 744f0000 74505000   C:\Windows\system32\rasman.dll
ModLoad: 744e0000 744ed000   C:\Windows\system32\rtutils.dll
ModLoad: 72790000 72796000   C:\Windows\system32\sensapi.dll
ModLoad: 71cd0000 71ce0000   C:\Windows\system32\NLAapi.dll
ModLoad: 71c50000 71c56000   C:\Windows\system32\rasadhlp.dll
ModLoad: 725f0000 725f6000   C:\Windows\System32\wship6.dll
ModLoad: 71c60000 71c84000   C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live\WLIDNSP.DLL
ModLoad: 71bf0000 71c28000   C:\Windows\System32\fwpuclnt.dll
eax=7eef8000 ebx=00000000 ecx=00000000 edx=7731f50a esi=00000000 edi=00000000
eip=7729000c esp=1592ff5c ebp=1592ff88 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!DbgBreakPoint:
7729000c cc              int     3
0:034> .reload
Reloading current modules
................................................................
.............................
0:034> u kernel32!LoadLibraryA
kernel32!LoadLibraryA:
76d94bc6 e9552d3f9b      jmp     12187920
76d94bcb 837d0800        cmp     dword ptr [ebp+8],0
76d94bcf 53              push    ebx
76d94bd0 56              push    esi
76d94bd1 57              push    edi
76d94bd2 7417            je      kernel32!LoadLibraryA+0xae (76d94beb)
76d94bd4 68004cd976      push    offset kernel32!`string' (76d94c00)
76d94bd9 ff7508          push    dword ptr [ebp+8]
0:034> u user32!GetCursorPos
USER32!GetCursorPos:
754e0e0d e98e69ca9c      jmp     121877a0
754e0e12 6a69            push    69h
754e0e14 6a01            push    1
754e0e16 ff7508          push    dword ptr [ebp+8]
754e0e19 e8a65cffff      call    USER32!NtUserCallTwoParam (754d6ac4)
754e0e1e 5d              pop     ebp
754e0e1f c20400          ret     4
754e0e22 33c0            xor     eax,eax
0:034> u user32!SetPhysicalCursorPos
USER32!SetPhysicalCursorPos:
75519f13 e958d9c69c      jmp     12187870
75519f18 6a75            push    75h
75519f1a ff750c          push    dword ptr [ebp+0Ch]
75519f1d ff7508          push    dword ptr [ebp+8]
75519f20 e89fcbfbff      call    USER32!NtUserCallTwoParam (754d6ac4)
75519f25 5d              pop     ebp
75519f26 c20800          ret     8
75519f29 90              nop

Quote from Kynox:
"*Edit*: Why do you have a gameguard bypass in a WoW hack? This whole thing seems to be some giant troll.

[CODE]

The above returns kernel32's imagebase, instead of loading "npggNT.des" which is a GameGuard DLL."

Code accompanying Kynox's post:
.text:10007920 sub_10007920    proc near               ; DATA XREF: DllMain(x,x,x)+382o
.text:10007920
.text:10007920 lpLibFileName   = dword ptr  4
.text:10007920
.text:10007920                 push    esi
.text:10007921                 mov     esi, [esp+4+lpLibFileName]
.text:10007925                 push    offset aNpggnt_des ; "npggNT.des"
.text:1000792A                 push    esi             ; char *
.text:1000792B                 call    _strstr
.text:10007930                 add     esp, 8
.text:10007933                 test    eax, eax
.text:10007935                 jz      short loc_10007946
.text:10007937                 pop     esi
.text:10007938                 mov     [esp+lpLibFileName], offset aKernel32_dll_0 ; "Kernel32.dll"
.text:10007940                 jmp     ds:GetModuleHandleA
.text:10007946 ; ---------------------------------------------------------------------------
.text:10007946
.text:10007946 loc_10007946:                           ; CODE XREF: sub_10007920+15j
.text:10007946                 push    0               ; dwFlags
.text:10007948                 push    0               ; hFile
.text:1000794A                 push    esi             ; lpLibFileName
.text:1000794B                 call    ds:LoadLibraryExA
.text:10007951                 pop     esi
.text:10007952                 retn    4
.text:10007952 sub_10007920    endp

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.