スクリプトの更新


オンラインゲームでは新しい要素を追加するために定期的にアップデートパッチが行われます。
バージョンアップに伴い以前作成したスクリプトが利用できなくなることが多いです。
これは新たな機能、すなわち、プログラムが挿入されることによってアドレスが後ろにずれこむからです。

例えば、以下の画像を参照してみましょう。
imageプラグインエラー : ご指定のファイルが見つかりません。ファイル名を確認して、再度指定してください。 (v2.PNG)
パッチ前 パッチ後
パッチ前と後でこのコードが存在するアドレス自体はパッチ後は後方にずれ込んでいること(012E960Ch→0136D12Ch)が分かります。
このようにパッチが行われたとしても元のソースコードが改変されない限りはパッチ後でもずれこんだコードがそのままの形で残ることになります。

よって、あらかじめパッチ前のアドレス周辺のバイト列または特徴的な命令列を保存しておき、パッチ後にCEのメモリスキャン機能によって検索することでパッチ後のアドレスを特定することが可能となります。
この手法はスクリプトのアドレス部をパッチ後のアドレスにずらす作業が主になるため「アドレスずらし」や「アドずらし」などと呼ばれることがあります。

バイト列の取得

CEのメモリビューアからバイト列をクリップボードにコピーすることが可能です。

メモリビューアでFileタブから"Save disAssembled output"を選択するか、逆アセンブルビューで"CTRL+C"を入力すると逆アセンブルの出力をクリップボードにコピーすることが可能です。
ここで"bytes"だけにチェックを入れると指定した範囲のバイト列をコピーすることができます。
この時、"SIFHT"キーを押したまま矢印キーを押してコピーしたいコードをハイライトさせた状態で"CTRL+C"を押すと自動的に領域を指定してくれるため便利です。


メモリビューア下部のバイナリビューからもバイト列をクリップボードにコピーすることができます。
こちらはマウスでコピーしたいバイナリをドラッグして赤色にハイライトさせた上で、"CTRL+C"を押すかまたは右クリックで出たメニューから"Copy to clipboard"を選択します。

画像の例では、
53 56 8B 74 24 0C 8B D9 8B CE 89 33 FF 15 84 20 9D 01 85 C0 74 2B 8B CE FF 15 84 20 9D 01 85 C0 74 1F 57 8B 3D 6C 00 66 01 8D A4 24 00 00 00 00 6A 00 FF D7 8B CE FF 15 84 20 9D 01 85 C0 75 F0 5F 5E 8B C3 5B C2 08 00
というバイト列が取得できます。

ではパッチ後にこのバイト列で検索をかけるとアドレスが更新できるかというとそうではありません。
直接アドレッシングを行っている命令がある場合、パッチ後に指定するアドレスが変化するためその部分のバイト列も変動します。
今回のようなメモリアドレッシング以外には分岐命令などでアドレスが直接指定される場合があります。

先ほどの命令についてパッチ前とパッチ後で比較してみましょう。
パッチ前 パッチ後

上記の例の場合、call dword ptr [********] や mov edi,[********] といった命令でアドレスを直接指定しています。
パッチ毎にこのアドレスが変動するため、それに伴いバイト列も変動することになります。

このような直接アドレッシングな命令に対応するバイト列についてはワイルドカード「*」(アスタリスク記号)に置き換えましょう。
ワイルドカードを利用することで、バイト列の検索時にワイルドカードの部分だけはいかなるバイト列にもマッチするものとして扱うことが可能です。

上記の例の場合は次のようにワイルドカードに置き換えます。
53 56 8B 74 24 0C 8B D9 8B CE 89 33 FF 15 ** ** ** ** 85 C0 74 2B 8B CE FF 15 ** ** ** ** 85 C0 74 1F 57 8B 3D ** ** ** ** 8D A4 24 00 00 00 00 6A 00 FF D7 8B CE FF 15 ** ** ** ** 85 C0 75 F0 5F 5E 8B C3 5B C2 08 00

これでパッチ後に更新するためのバイト列を用意することができました。
なお、ワイルドカードで置き換えたバイト列の部分は直接アドレッシングで指定するアドレスをリトルエンディアンで表記した形になっていることが分かるでしょう。

バイト列の検索

バイト列を検索するためにはメモリビューアの"Search"タブから"Find memory"を選択するか、バイナリビュー上で"CTRL+F"を入力すると以下の"Find"ウインドウが開きます。


ここで検索のタイプは[Array of]byte にチェックを入れます。
Fromパラメータで指定したアドレスからToパラメータで指定したアドレスまでの範囲を検索することになります。

Fromパラメータは00400000hまたはパッチ前のアドレス値を入れるのが普通です。
Toパラメータは実行領域をカバーできる程度のアドレスを適当に指定します。
この時Toのアドレスが大きすぎると、指定したバイト列が発見できないまま延々とスキャンが続行されCEがハングアップする(応答なしの状態になる)可能性があります。

メモリスキャンの結果、指定したバイト列と一致する直近のアドレスがバイナリビューに反映されます。
バイナリビューを右クリックして"Goto address"を選択することでこのアドレスを取得することができます。

命令列の取得

CEではバイト列だけでなくアセンブリコードの検索を行うことも可能です。
バイト列をコピーした時と同様に"Save disAssembled output"機能を利用します。
この時、"opcode"のみにチェックをいれましょう。

保存された命令列は以下の通りとなります。
push ebx
push esi
mov esi,[esp+0C]
mov ebx,ecx
mov ecx,esi
mov [ebx],esi
call dword ptr [01A900E4]
test eax,eax
je 00409951
mov ecx,esi
call dword ptr [01A900E4]
test eax,eax
je 00409951
push edi
mov edi,[016FE230] : [08341CDD]
lea esp,[esp+00000000]
push 00
call edi
mov ecx,esi
call dword ptr [01A900E4]
test eax,eax
jne 00409940
pop edi
pop esi
mov eax,ebx
pop ebx
ret 0008

※ 上記の例にある"mov edi,[016FE230] : [08341CDD]"のようにコメント文がコピーした命令列に付加される場合があるので注意してください。

命令列の検索

命令列を検索するためにはメモリビューアの"Search"タブから"Find assembly code"を選択します。
CEの"Assembly scan"機能ではメモリ領域の中から入力した命令列を高速にスキャンすることができます。
また、この機能はワイルドカードにも対応しています。

取得したスクリプトに対してパッチ毎に変化する可能性がある部分をワイルドカードに置き換えましょう。
先ほど取得した命令の場合は次のように置き換えられます。

基本的にアドレス指定部分をワイルドカードに置き換えます。
ジャンプ命令やコール命令、プッシュ命令やメモリアドレッシング部分がこれに該当します。
関数によってはパッチ後に変数が追加されてオフセットがずれる可能性があるので、オフセット部分をワイルドカードに置き換えることもあります。

パッチ後のクライアントで検索をかけた結果1件ヒットしました。成功です!

"Assembly scan"機能は""後方検索しか行いません。
検索の範囲はFromパラメータとToパラメータで指定する必要があります。
Fromパラメータの値は"Assembly scan"ビューを開いた時に現在選択している命令のアドレスが自動的にセットされるので注意してください。
最終更新:2014年07月23日 11:01