Ein Hinweis vorneweg: Dieser Post überschneidet sich thematisch zu großten Teilen mit “A shitload of Reversing” – mit dem Unterschied, dass dieser Post das Ganze etwas technischer beleuchtet und einige Codesnippets beiliegen.
–
Wie oft hat man es im “User Bewertung” Forum gesehen: RO Client 100%.
Abgesehen davon, dass 80% der dort geposteten Selbsteinschätzungen maßlose Überschätzungen waren, hat mich persönlich dieser Punkt immer ein wenig amüsiert. Ich behaupte einfach mal, dass ich zu dem halben Dutzend gehöre, die sich (im westlichen Raum zumindest, keine Ahnung was in Asien an gelangweilten Codern rumläuft) mit am besten mit dem RO Client auskennen. Schlicht und einfach aufgrund der Tatsache, dass ich dieses Teil nun seit 2 Jahren auseinander nehme und so ziemlich jede Funktion schon einmal gesehen habe.
Klar, für den Betrieb eines normalen Freeshards reicht es aus, eine Exe ‘diffen’ zu können und mit dem Grundschleim der GRF (Monster, Maps, Aura, etc.) vertraut zu sein. Ich persönlich nehme die Dinge ganz gerne auseinander; mal weil es mich einfach interessiert und mal weil mir einfach langweilig ist. Es ist teilweise faszinierend was im RO Client, den so viele zu 100% beherrschen, alles drin steckt. (Nein, das macht den RO Client noch immer noch innovativ.)
Meine “Arbeit” am RO Client beginnt meistens mit einer kleinen Idee, den RO Client zu modifizieren. Unabhängig davon, ob diese anfängliche Idee durchgesetzt wird – meistens stoße ich dabei auf Aspekte, welche neue Ideen bringen und so dauert es nicht lange, bis ich mich durch die Masse der Funktionen klicke und sich eine neue Idee herausbildet. Auf diese Art sind beispielsweise die serverseitigen GM Listen oder das charakterbasierte Geschlecht entstanden.
Soweit so gut – irgendwann hat man dann so viel ausprobiert, dass man sich nach neuen Herausforderungen sehnt. Man verweise hier nur, auf meine anfänglichen Versuche am Window Manager und dem kurz darauf folgenden Fortschritten.
Die bisher größte Herausforderung war jedoch etwas scheinbar total banales: Das Verwenden einer kRO Main EXE (aka Ragexe). Klingt simpel, bringt aber einige Tücken mit sich. Seit Herbst 2008 sind die kRO Main Exen mit ASProtect gepackt, haben massive Packetänderungen und verhalten sich auch allgemein sehr .. anders.

Soweit so gut. Man hätte sich nun die Mühe machen können und ASProtect entpacken. Aber einerseits lag mein Hauptinteresse darin, eine für Dritte fast unbenutzbare Exe zu verwenden, daher habe ich mich dazu entschlossen, ASProtect zu belassen und bin das Ganze etwas anders angegangen. Zuerst die Exe gestartet und mit einem kleinen C Tool den Speicher gedumpt – wie alle RO Exen lag der Code weiterhin im Bereich ab 410000h, sodass zumindest der Code schnell gedumpt war. Zwar fehlten Teile des .data Segments und die Ressourcen, aber das spielt eigentlich keine Rolle. Zu dem Codedump noch schnell einen DOS/NT Header generieren (genauer gesagt per XVI einen bestehenden hineinkopieren..) und man konnte die Exe zumindest mit IDA auseinander nehmen. Die fehlenden Informationen, hauptsächlich Importfunktionen, konnten über die Sakexe vom selben Datum relativ schnell herausgefunden werden, somit stellte der Mangel einer lauffähigen, entpackten Exe vorerst kein Problem dar. Vorerst. Oder anders formuliert: Bis ich den ServiceType auf America stellte. Oh, richtig. Die Endlosschleife, die den Client freezt, wenn der ServiceType nicht Korea ist. Eben schnell “rausdiffen”? Nein, ganz so einfach leider nicht, da man dank ASProtect die Exe nicht ohne Komplikationen patchen konnte.
Vermutlich hätte sich dieses Problem wesentlich eleganter lösen lassen, aber ich habe mich für die umständliche Variante entschieden: Einen Launcher. Im Grunde genommen hatte dieser Launcher keine andere Aufgabe, als die Ragexe.exe (unter Angabe meiner server.lst als sclientinfo) als ‘suspended’ zu starten und einen Hook zu installieren, welcher dann den Diff einspielt. In der Theorie total einfach, in der Praxis sah der Code dann schon etwas merkwürdig aus (komplettes Listing):
*(BYTE* )&buf[61] = 0xE9; // opcode jmp
*(WORD* )&buf[62] = 0×0888; // mov byte ptr [eax], cl
*(BYTE* )&buf[64] = 0×40; // inc eax
*(BYTE* )&buf[65] = 0xB9; // mov ecx, …
*(DWORD*)&buf[66] = ((int)&ptr[87] – (int)SetUnhandledExceptionFilter – 5);
*(WORD* )&buf[70] = 0×0889; // mov [eax], ecx
Nun ging es zum interessanten Teil. Login- und Charserver stellten kein Problem dar; keinerlei Änderungen. Der Mapserver begrüßte mich mit diversen ‘unknown Packets’ und einem sofortigen Verbindungsabbruch. Es stellte sich heraus, dass die Ragexe mittlerweile eine Packet Encryption verwendet (zumindest teilweise), welcher aber glücklicherweise nicht allzuviel Implementierungsaufwand mit sich brachte. Die geänderte ID des Zone Login Packets machte auch noch Sinn, aber als sich herausstellte, dass jedes einzelne Packet eine andere ID hatte, entwickelte sich das zu einem ernsten Problem. Lösung? Ganz klar, erstmal Kaffee kochen, duschen und eine Nacht darüber schlafen.
Am nächsten Morgen war dann auch die rettende Idee gekommen: Eine Analyse des internen Packetparsers und ein Abgleich dieser Hashtabellen mit der aktuellen Sakexe. Dazu noch eine Liste der von eA verwendeten Packets (clif.c Codeanalyse im Schnellcodingverfahren und ohne Buffersupport…) und es sah schonmal recht vielversprechend aus. In den ersten Testläufen wurden rund 85% der Packetänderungen korrekt erkannt.
So langsam sollte man es doch dann man geschafft haben, oder? Immerhin waren zu diesem Zeitpunkt bereits zig Stunden in diese Aktion investiert worden. Weit gefehlt. Anscheinend hatte Gravity auch jegliche Spawnpakete “zergrützt”. Diese Änderungen waren auf den ersten Blick schnell reverst (einige XOR/ADD Operationen und eine geänderte Reihenfolge), doch funktionieren wollte es damit noch immer nicht so ganz. An diesem Punkt musste ich aus Zeit- und Nervengründen leider vorerst aufgeben und habe seitdem auch keine Zeit mehr gefunden, mich dieser Problematik ausgiebig zu widmen. Schade eigentlich, denn es war seit langem mal wieder eine interessante Herausforderung. Aber anscheinend bin ich noch sehr weit von den “100% RO Client” entfernt. Ich sollte mir eine Scheibe von den selbsternannten Profis im gAthena abschneiden.