Apex = Defeated?

Anderthalb Monate ist es her, seid der erste Post über Apex entstanden ist. Nun bin ich an dem Punkt angelangt, Apex als besiegt zu bezeichnen. Zwar weis ich noch lange nicht, wie all die unzähligen Apex Module arbeiten, aber es reicht für uns einen Hack zu schreiben ohne das Apex (frei nach Archimedes) unsere Kreise stört , Mir ist klar dass dieser Post vielleicht von anderen Kopiert wird und als Vorlage für andere Hacks benutzt wird, daher werde ich keinen kompletten Source posten.

Im letzten Post wurde erwähnt dass Apex uns nach dem Hook von End2DOptimized und RenderString Kicks verteilt. Auch sollte im Hinterkopf behalten werden, dass Apex uns nicht selbst kickt, sondern nur Daten an den zentralen Server weiterleitet. Des weiteren sollten wir uns in das Gedächnis rufen, dass die Detour-Library die ersten 6 Bytes einer Funktion mit einem JMP ersetzt. Das alles deutet darauf hin, dass Apex irgendwie den Funktionsanfang prüft, ob dort Hooks gesetzt sind oder nicht. Prompt wurde CheatEngine gezückt und der Debugger attached, welcher in auf Lesezugriffe am Anfang der gehookten End2DOptimized Funktion achtet. Das selbe wäre auch mit OllyDbg möglich gewesen, aber CheatEngine ist in diesem Fall bequemer zu bedienen.

ApexHashing

Mit allein diesem Resultat fangen wir wenig an. Wichtig ist zu wissen, dass generell 0x7XXXXXXX Adressen in den Kernelspace zeigen. Mit dem Debugger finden wir, dass 0x760575B2 in der Funktion IsBadReadPointer liegt und diese wohl unseren Funktionsanfang prüft. Der Call dazu erfolgt allerdings auf dem 0x0CCXXXXX Adressbereich. Wir wissen ja inzwischen, das Apex zwei große Module in den Speicher mapped, allerdings ist keines davon annährend in diesem Bereich. Ein Glück, dass VirtualAlloc gehooked ist und wir somit die dynamischen Speicherreservierungen mitbekommen. Darin finden wir:

14:08:20 Virtual Alloc! Size: 7d90, Addr: 0xcce0000, allocType: 1000, flProtect: 40

Dabei bedeutet der flProtect-Parameter PAGE_EXECUTE_READWRITE, also wird in diesem Memory-Bereich wohl u.a ausführbarer Code gespeichert. Ein weiteres dynamsches Modul was Apex!

Dass wir nun wissen wo der Speicherbereich herkommt mag noch nicht so interessant sein, dafür wird es jetzt spannend: Was macht denn der Code, der aus diesem Adressbereich auf unsere Funktion zugreift? Ein Blick mit dem Debugger wirft folgendes Bild:

Fangen wir mit dem Stack an: Es werden vor dem Funktionsaufruf 2 Argumente auf den Stack gepushed, also handelt es sich um einen __cdecl-call ohne irgendwelche Argumente in Registern. Das erste Argument (= das grau hinterlegte) zeigt auf einen Speicherbereich in der CShell.dll . Wenn wir mit dem Debugger dieser Adresse folgen, so merken wir, dass wir uns in einer Funktion der CShell befinden. Das weitere ausführen lässt uns immer wieder an dieser Stelle breaken, allerdings immer mit anderen Paramentern. Der erste Parameter zeigt stehts auf den Funktionsanfang bzw in eine Funktion der CShell, der zweite Parameter scheint die Länge der Funktion zu sein. Den Kommentaren in dem Screenshot ist zu entnehmen, was dieser Code macht. Da ich diese Schritte nicht nochmal einzeln erklären will, hier die Zusammenfassung von dem Algorithmus: Die Funktion bekommt eine Adresse und eine Funktionslänge übergeben und addiert in 4er Blöcken jeweils den Code, der an dieser Stelle zu finden ist. Danach wird der “Code-Zeiger” um 4 erhöt und zeigt wieder auf neuen Code. Hier kommt es oftmals zu einem Überlauf des DWORDs EDI, daran stört sich das Programm aber nicht. Zurückgegeben wird dieser Hash-Wert des Funktionsblocks.

Apex hashed also den Funktionscode und liefert dieses Resultat im Anschluss an den Server. Da wir direkt die ersten 6 Bytes einer Funktion verändern, verändert sich auf die Hash-Value und somit folgt ein Kick!

Wie umgehen wir den Spass nun? Fest steht, dass wir (trotz gehookten Funktionen) den richtigen Hash-Wert zurückgeben müssen. Zwei Möglichkeiten bieten sich uns:

  1. Wir hooken die oben dargestellte Funktion, prüfen ob der “Funktionsanfang” innerhalb einer gehookten Funktion liegt und liefern den zuvor geloggten originalen Wert zurück
  2. Da Apex unmöglich den Hash von ca. 3000 Funktionen übermitteln kann, schauen wir wo diese Hashes zu einem großen Hash zusammenfließen und manipulieren diesen um endgültig Ruhe zu haben.

Variante 1 Funktioniert für Funktionen, die innerhalb von Wolfteam.exe liegen gut, da Apex die ganze Wolfteam.exe zu einem Hash verwandelt:

14:59:39 Memory Seg 0x334b5af5 with length 0xc resulted in the hash: 0x82e6f0b4
14:59:39 Memory Seg 0x334b5b05 with length 0xc resulted in the hash: 0x82fd70b4
14:59:39 Memory Seg 0x401000 with length 0x2b2cfc resulted in the hash: 0xe9a8488a

Zur Erinnerung: 0×0040000 ist meist die BaseAdress für die “Main Executable”, 0×1000 ist meist der Offset in die .text Section. Variante 2 klingt doch wesentlich schöner, also verfolgen wir, was mit dem gerade eben erzeugten Hash passiert.

push    ecx -> Länge der Funktion pushen (Arg 2)
push    edx -> Startadresse der Funktion pushen (Arg 1)
mov     ecx, esi -> (thiscall Convention, wird aber nie verwendet)
call    sub_69BF -> Hash Memory call!
add     ebp, eax -> Hash zu EBP hinzufügen
mov     eax, [esi+4] 
push    eax
inc     edi
call    near ptr 806F2F0h
add     esp, 4
cmp     edi, eax -> Sind noch Daten vorhanden?
jb      short loc_6D30 -> Wir Springen hoch um die nächste Mem-Section zu hashen
mov     [esp+14h+var_4], ebp -> Alle Hashes in Variable zwischenspeichern
mov     eax, [esp+14h+var_4] -> Und diese Variable in EAX zum zurückgeben
[.... ret Stuff]

Ansetzen müssen wir also ganz am Ende. Wir könnten endweder direkt einen festen statischen Wert reinpatchen oder erstmal Detouren, um den Wert bei einer unveränderten Binary festzustellen. Ich habe mich für letzteren Weg entschieden.

Der Detour läuft und auch der Apex Kick findet nicht mehr statt. Wir können nun fröhlich in allen Modulen rumhooken, ohne dass etwas passiert! Also können wir mit Funktionen wie dem PlayerArray und auch der WorldToScreen Funktion weitermachen, die im nächsten Teil besprochen werden :)

Greez

17 people like this post.
    • tr4ceflow
    • 3. Jul. 2014 7:02pm

    Wie lange saßt du jetzt effektiv hier an dem Problem?

      • Easysurfer
      • 3. Jul. 2014 11:52pm

      Das finden und fixxen dieser “Hash”-Funktion ging relativ schnell, da man ja direkt zum interessanten Code gelangt. Mit loggen aller Values, schreiben des Detours und natürlich debuggen vielleicht 2 Stunden.

    • Zate
    • 8. Jul. 2014 10:31pm

    Hi, scheint ja schwerer zu sein als ich gedacht habe. Ich habe zwar einen Bypass und kann CE permanent offen lassen/ Adressen suchen, doch kann CE nicht an Wolfteam.bin anhängen. Somit hängt das ganze also mit Apex zusammen, dass es nicht funktioniert oder habe ich etwas falsch konfiguriert? Sehr schön beschrieben, Danke!

      • Easysurfer
      • 9. Jul. 2014 9:30am

      Der Blogpost beschäftigt sich nur mit dem Bypassen von dem “Anti-Hook” Modul. Andere Module von Apex checken z.B. die offnen Programme und auch angehängten Debugger.
      Du kannst mit ScyllaHide (gibt es afaik nicht nur als OllyDbg Plugin) den Debugger vor Wolfteam verstecken und somit dieses Apex Modul umgehen.

      Wie Funktioniert dein Bypass für CE?
      Greez

    • Zate
    • 10. Jul. 2014 1:05am

    Hi, Danke für den Hinweis. Der Bypasser selber ist nicht von mir, aber da er momentan noch relativ unentdeckt scheint, will ich ihn hier nicht nennen. Du kannst mich sonst unter meiner Email erreichen, dann werde ich dir Näheres bekannt geben. Auf jeden Fall kann ich leider keinen Pointer-Scan durchführen oder den Debugger attachen. Aber ich werde es mal die Tage mit ScyllaHide versuchen.

    Greetz

  1. Noch keine TrackBacks.