CConnection::Startupクラス関数を読み解く
この記事は次のステップに進む際の前準備的な物です。
コードから実装アイディアを捻り出すには、そのコードを読み解き、動きを把握することが重要です。
では、以下のコードを見てみましょう。
F0065d540: 0065d540 68d02f8c00 push dword L008c2fd0 0065d545 6801010000 push dword 000000101h 0065d54a ff15fc267b00 call dword [WS2_32.dll_73] 0065d550 85c0 test eax,eax 0065d552 7416 jz C0065d56a ; if { 0065d554 6864357e00 push dword S007e3564 0065d559 e852c8eeff call near F00549db0 0065d55e 83c404 add esp,byte +004h 0065d561 ff1518277b00 call dword [WS2_32.dll_74] 0065d567 32c0 xor al,al 0065d569 c3 ret ; } C0065d56a: 0065d56a 56 push esi 0065d56b 68d4347e00 push dword S007e34d4 0065d570 ff158c217b00 call dword [L007b218c] 0065d576 8b3590217b00 mov esi,dword [L007b2190] 0065d57c 68b8347e00 push dword S007e34b8 0065d581 50 push eax 0065d582 a364318c00 mov [L008c3164],eax 0065d587 ffd6 call esi 0065d589 a368318c00 mov [L008c3168],eax 0065d58e a164318c00 mov eax,[L008c3164] 0065d593 685c357e00 push dword S007e355c 0065d598 50 push eax 0065d599 ffd6 call esi 0065d59b 833d68318c0000 cmp dword [L008c3168],byte +000h 0065d5a2 8b3590267b00 mov esi,dword [L007b2690] 0065d5a8 a360318c00 mov [L008c3160],eax 0065d5ad 751c jnz C0065d5cb ; if { 0065d5af 8b0d00277b00 mov ecx,dword [WS2_32.dll_13] 0065d5b5 6a00 push byte +000h 0065d5b7 6844357e00 push dword S007e3544 0065d5bc 6824357e00 push dword S007e3524 0065d5c1 6a00 push byte +000h 0065d5c3 890d68318c00 mov dword [L008c3168],ecx 0065d5c9 ffd6 call esi ; } C0065d5cb: 0065d5cb 833d60318c0000 cmp dword [L008c3160],byte +000h 0065d5d2 751c jnz C0065d5f0 ; if { 0065d5d4 8b1504277b00 mov edx,dword [WS2_32.dll_10] 0065d5da 6a00 push byte +000h 0065d5dc 6844357e00 push dword S007e3544 0065d5e1 6804357e00 push dword S007e3504 0065d5e6 6a00 push byte +000h 0065d5e8 891560318c00 mov dword [L008c3160],edx 0065d5ee ffd6 call esi ; } C0065d5f0: 0065d5f0 e8dbfeffff call near F0065d4d0 0065d5f5 8bc8 mov ecx,eax 0065d5f7 e8b4f8ffff call near F0065ceb0 0065d5fc b001 mov al,byte 001h 0065d5fe 5e pop esi 0065d5ff c3 ret 0065d600 8bc1 mov eax,ecx 0065d602 33c9 xor ecx,ecx 0065d604 894804 mov dword [eax+004h],ecx 0065d607 89483c mov dword [eax+03ch],ecx 0065d60a c3 ret
2011-12-01aRagexe.exe(iRO)をdispeでディスアセンブルしたものの抜粋で、ROのC++のソースコードのCConnection::Startup関数にあたります。
しかしながら、この状態は訓練している人なら別ですが、普通の人には難読極まりなく、読むことさえ放棄したくなるような代物です。
著者はz80のコードをハンドアセンブルして、バイナリエディタでプログラムを組むなんて経験をして育ってきているので、割と苦にならない人間ですが、それでもコードが大きくなるとシンドイとは感じます。
やはり可読性を上げる作業は必須です。
このサイトで公開しているコードに、ラベルや関数名、変数名を付けていますが、これは以前、公式からダウンロード出来た「ハイプリーストになりたい」(HighPriest_081105_JPN.exe)というミニゲームのデバッグシンボル情報から導き出し、現行ソースコードに至るまで、ある程度保守していた事によるものです。
現行exeでも、コードを照らし合わせてラベル定義をすることは可能なので、これを適応したものを提示します。ツールとしては、保守していくことも考慮に入れて、ここのサイトで紹介しているIDAを使う事をお勧めします。
では実際、読み易さがどこまで向上するかを見てみましょう。
CConnection__Startup proc near ; CODE XREF: WinMain(x,x,x,x) push offset CConnection_s_wsaData ; lpWSAData push 101h ; wVersionRequested call ds:WSAStartup test eax, eax jz short loc_65D56A push offset aFailedToLoadWi ; "Failed to load Winsock library!" call ErrorMsg add esp, 4 call ds:WSACleanup xor al, al retn ; --------------------------------------------------------------------------- loc_65D56A: push esi push offset aWs2_32_dll_0 ; "ws2_32.dll" call ds:LoadLibraryA mov esi, ds:GetProcAddress push offset aSend ; "send" push eax ; hModule mov CConnection_s_wsmodule, eax call esi ; GetProcAddress mov CConnection_s_wsSend, eax mov eax, CConnection_s_wsmodule push offset aRecv ; "recv" push eax ; hModule call esi ; GetProcAddress cmp CConnection_s_wsSend, 0 mov esi, ds:MessageBoxA mov CConnection_s_wsRecv, eax jnz short loc_65D5CB mov ecx, ds:send push 0 ; uType push offset aModuleHookingE ; "Module Hooking Error" push offset aGetprocaddress ; "GetProcAddress(¥"send¥") Failed." push 0 ; hWnd mov CConnection_s_wsSend, ecx call esi ; MessageBoxA loc_65D5CB: cmp CConnection_s_wsRecv, 0 jnz short loc_65D5F0 mov edx, ds:recv push 0 ; uType push offset aModuleHookingE ; "Module Hooking Error" push offset aGetprocaddre_0 ; "GetProcAddress(¥"recv¥") Failed." push 0 ; hWnd mov CConnection_s_wsRecv, edx call esi ; MessageBoxA loc_65D5F0: call CRagConnection__instanceR mov ecx, eax call sub_65CEB0 mov al, 1 pop esi retn CConnection__Startup endp
どうでしょう?、少しはやる気が出てきませんか?
コードとしては特に難しい事をしている訳でもないので、先頭から順々に解釈していくことで、元のC++のソースコードを想像することも出来ます。筆者の頭の中では上記のコードを眺めていると、以下のようなコードが頭の中に見えてきます。慣れないうちは、アセンブリコードの行を処理ごとに色分けしてみるのも良いかもしれません。
typedef int (WSAAPI *tWS32_send)( SOCKET s, const char *buf,int len,int flags ); typedef int (WSAAPI *tWS32_recv)( SOCKET s, char *buf,int len,int flags ); WSADATA CConnection::m_s_wsaData; tWS32_send CConnection::m_s_wsSend; tWS32_recv CConnection::m_s_wsRecv; HMODULE CConnection::m_s_wsmodule; BOOL CConnection::Startup() { if( WSAStartup( 0x0101,&m_s_wsaData) ) { ErrorMsg( "Failed to load Winsock library!" ); WSACleanup(); return FALSE; } // loc_65D56A m_s_wsmodule = LoadLibraryA( "ws2_32.dll" ); m_s_wsSend = (tWS32_send)GetProcAddress( m_s_wsmodule, "send" ); m_s_wsRecv = (tWS32_send)GetProcAddress( m_s_wsmodule, "recv" ); if( !m_s_wsSend ) { MessageBoxA( NULL,"GetProcAddress(¥"send¥") Failed.","Module Hooking Error",MB_OK); } // loc_65D5CB if( !m_s_wsRecv ) { MessageBoxA( NULL,"GetProcAddress(¥"recv¥") Failed.","Module Hooking Error",MB_OK); } // loc_65D5F0 CRagConnection::instanceR() -> sub_65CEB0(); return TRUE; }
call ds:WSAStartupで関数を呼び出し、
test eax,eaxで戻り値を評価、
jz short loc_65D56Aで戻り値がゼロ以外ならエラーメッセージの表示といった感じに読み続けていきます。
難しいと感じるのは全体を見て、膨大な量だと感じてしまうためで、1つ1つを見ると、実は単純な処理の羅列でしかありません。
ここまでくればsendとrecvのファンクションのアドレスが発覚したことになり、パケット受信処理をフックしてパケットを捕捉することにより、独自の処理を実装する事が可能です。
ただ、コードスクランブルが一部で実装されたことにより、そう単純には行かなかったりしますが、これはまた次回以降に続きます。
ディスカッション
コメント一覧
RCXですがパケット変更によって徐々にタイマーやエフェクトに問題が生じています(演奏にファイアウォール重なると稀にクラッシュするなど
自分の実力でどうにかなるかわかりませんが
RCXのソースコードを頂けると大変うれしいです。
ソースコードを開示しないという事に関しては、大分前に結論が出ていたかと思います。
任意のパケットが送信できてしまう等、少なからず悪用可能なコードが含まれる事も上げられます。
ボク一個人として、信用の置ける知人にコードを託すという話ならば有だとは考えますが、ユーザーへの開示は公平性の観点から見ても有りません。過去にソースコードを開示して、質問攻めにあい精神的に疲弊したという事もあります。
全ての人が良識者で善人であることは保証できないので、これは致し方ありません。
また、フリーで作れる開発環境構築や、開示しても良い技術資料に関する記事は書いてきましたが、率先して作ってみようという人も現れなかったため、技術資料の方の記事の更新も停止しています。
やる気のある人が居れば、3Dマップ上へマーカーを投影する所まではやろうかと思っては居ましたが…。