制作日記

こういうの作った方が、逃げにくいじゃない

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

varrandomでのmugen落ちとか

http://oki6761.blog23.fc2.com/blog-entry-2464.html

あと、mugen落ちの原因説明のための細かい仕様解説とか

まず、mugenが内部で使用している乱数について
http://drabs.blog40.fc2.com/blog-entry-215.html
要は0~7FFFhの乱数になっている

で本題その1、varrandomの仕様補足
・rangeの最小値、最大値指定は逆にしても大丈夫、勝手に内部で入れ替える
・最小値~最大値の乱数を生成するという仕様は嘘
 生成範囲は概ね最小値~min(最小値+7FFFh、最大値)、例外あり
 生成アルゴリズムは次の通り
 1.元ネタ乱数生成(上述の通り、0~7FFFh)
 2.乱数の生成幅を算出(最大値 - 最小値 + 1)
 3.1の乱数を生成幅で割った余りを算出(乱数 mod 生成幅)
 4.3の剰余に乱数生成範囲の最小値を加算

次、本題2、生成範囲にある値を指定した場合に落ちる原因
 3.1の乱数を生成幅で割った余りを算出(乱数 mod 生成幅)
これで0除算してる
以下、アセンブラ見ながら解説
0046F0DB  |> E8 573E0200    CALL winmugen.00492F37
# 元ネタ乱数生成(eaxに入る)
0046F0E0 |. 2BF7 SUB ESI,EDI
# 最大値(esi) - 最小値(edi)を計算、ここでオーバーフローして結果(esi)が-1になる
0046F0E2 |. 99 CDQ
# eaxを64bit(edx:eax)に拡張する、割り算の前準備
0046F0E3 |. 46 INC ESI
# さっきの計算結果(esi)に1加算、0になる
0046F0E4 |. F7FE IDIV ESI
# esiで乱数(edx:eax)を割る、ここで0除算
# edx=剰余、eax=商
0046F0E6 |. 03D7 ADD EDX,EDI
#剰余に最小値を加算、これがvarrandomで代入される値



ついでにvarrandom全体コードと懇切丁寧な解説をどうぞ
ちなみに、実際にそういう順序で解析したわけではない
0046F08F  |> 8B9C24 A010000>MOV EBX,DWORD PTR SS:[ESP+10A0]          ;  Case 6 of switch 0046E830
0046F096 |. 8D4E 24 LEA ECX,DWORD PTR DS:[ESI+24]
0046F099 |. 6A 01 PUSH 1
0046F09B |. 51 PUSH ECX
0046F09C |. 53 PUSH EBX
0046F09D |. E8 3E85F9FF CALL winmugen.004075E0
0046F0A2 |. 8BE8 MOV EBP,EAX
0046F0A4 |. 83C4 0C ADD ESP,0C
0046F0A7 |. 85ED TEST EBP,EBP
0046F0A9 |. 7C 51 JL SHORT winmugen.0046F0FC
0046F0AB |. 83FD 3B CMP EBP,3B
0046F0AE |. 7F 4C JG SHORT winmugen.0046F0FC
0046F0B0 |. 8D56 18 LEA EDX,DWORD PTR DS:[ESI+18]
0046F0B3 |. 6A 01 PUSH 1
0046F0B5 |. 52 PUSH EDX
0046F0B6 |. 53 PUSH EBX
0046F0B7 |. E8 2485F9FF CALL winmugen.004075E0
0046F0BC |. 83C6 30 ADD ESI,30
0046F0BF |. 6A 01 PUSH 1
0046F0C1 |. 56 PUSH ESI
0046F0C2 |. 53 PUSH EBX
0046F0C3 |. 8BF8 MOV EDI,EAX
0046F0C5 |. E8 1685F9FF CALL winmugen.004075E0
0046F0CA |. 8BF0 MOV ESI,EAX
0046F0CC |. 83C4 18 ADD ESP,18
0046F0CF |. 3BF7 CMP ESI,EDI
0046F0D1 |. 7D 08 JGE SHORT winmugen.0046F0DB
0046F0D3 |. 8BF7 MOV ESI,EDI
0046F0D5 |. 894424 18 MOV DWORD PTR SS:[ESP+18],EAX
0046F0D9 |. 8BF8 MOV EDI,EAX
0046F0DB |> E8 573E0200 CALL winmugen.00492F37
0046F0E0 |. 2BF7 SUB ESI,EDI
0046F0E2 |. 99 CDQ
0046F0E3 |. 46 INC ESI
0046F0E4 |. F7FE IDIV ESI
0046F0E6 |. 03D7 ADD EDX,EDI
0046F0E8 |. 33C0 XOR EAX,EAX
0046F0EA |. 8994AB 400E000>MOV DWORD PTR DS:[EBX+EBP*4+E40],EDX
0046F0F1 |. 5F POP EDI
0046F0F2 |. 5E POP ESI
0046F0F3 |. 5D POP EBP
0046F0F4 |. 5B POP EBX
0046F0F5 |. 81C4 8C100000 ADD ESP,108C
0046F0FB |. C3 RETN
0046F0FC |> 55 PUSH EBP
0046F0FD |. 68 88C04A00 PUSH winmugen.004AC088 ; ASCII "randomized var out of range (%i)"
0046F102 |. 53 PUSH EBX
0046F103 |. E8 2865FAFF CALL winmugen.00415630
0046F108 |. 83C4 0C ADD ESP,0C
0046F10B |. 33C0 XOR EAX,EAX
0046F10D |. 5F POP EDI
0046F10E |. 5E POP ESI
0046F10F |. 5D POP EBP
0046F110 |. 5B POP EBX
0046F111 |. 81C4 8C100000 ADD ESP,108C
0046F117 |. C3 RETN


これ見てすぐにわかるのは、↓は警告表示してRETしてる
つまりエラーハンドリング、ここに飛ぶのは全部エラー、エラー内容は警告のフォーマットで察する
0046F0FC  |> 55             PUSH EBP
0046F0FD |. 68 88C04A00 PUSH winmugen.004AC088 ; ASCII "randomized var out of range (%i)"
0046F102 |. 53 PUSH EBX
0046F103 |. E8 2865FAFF CALL winmugen.00415630
0046F108 |. 83C4 0C ADD ESP,0C
0046F10B |. 33C0 XOR EAX,EAX
0046F10D |. 5F POP EDI
0046F10E |. 5E POP ESI
0046F10F |. 5D POP EBP
0046F110 |. 5B POP EBX
0046F111 |. 81C4 8C100000 ADD ESP,108C
0046F117 |. C3 RETN


この関数呼び出しは、他所の類似箇所とかも見ればわかるけど
printfみたいな可変長引数で、関数の右側から引数を積んでる
よって、警告文内の"%i"に入る値は、直前にpushしているebp直後にpushしているebx
0046F0FC  |> 55             PUSH EBP
0046F0FD |. 68 88C04A00 PUSH winmugen.004AC088 ; ASCII "randomized var out of range (%i)"
0046F102 |. 53 PUSH EBX
0046F103 |. E8 2865FAFF CALL winmugen.00415630


実際に、↑に飛ぶ箇所を見てみる
0046F0A7  |. 85ED           TEST EBP,EBP
0046F0A9 |. 7C 51 JL SHORT winmugen.0046F0FC
0046F0AB |. 83FD 3B CMP EBP,3B
0046F0AE |. 7F 4C JG SHORT winmugen.0046F0FC

testの後にjlは負数かどうかの確認
直後は3Bh(=59)以下かの確認
さっきの警告文で明白だけど、こっちの確認内容からも察しやすい
これはvarの範囲チェック、よってebpはvarの番号

ところで、ここを処理してるときのESIの中身をデバッガで確認すると、ステコン情報のアドレスが入っている
中身はこんな感じ、dataの内容はステコン種別によっていろいろ
概ね、4byte*3が1組になってオプション1つを表す
varrandomはオプション3つ(rangeは2つ指定するから、1オプションで2つ分)が入っていると予想できる
    void *trigger;
int enable;
int persist;
int ignore;
int Id;
int data[20];


それを踏まえて、ebp=varの番号(オプション"v"の値)を取得する箇所はというと
0046F0A2  |. 8BE8           MOV EBP,EAX

ここでeaxから代入されていて
eaxは関数の戻り値に使われるんだから、このeaxは直前で呼び出してる↓の戻り値のはず
0046F09D  |. E8 3E85F9FF    CALL winmugen.004075E0

0046F096  |. 8D4E 24        LEA ECX,DWORD PTR DS:[ESI+24]
0046F099 |. 6A 01 PUSH 1
0046F09B |. 51 PUSH ECX
0046F09C |. 53 PUSH EBX
0046F09D |. E8 3E85F9FF CALL winmugen.004075E0


引数の内容もそれっぽい、ebxは確認してみるとキャラの先頭アドレス
ecx=esi+24hでesiは上述の通りステコンの先頭アドレス
これに24h足すとdata部分を指す、キャラの先頭アドレス渡してる辺り、それっぽい
push 1はわからん、無視

計算式の先頭アドレスとキャラのアドレス渡せば整数値系の計算はできるのかな?
小数計算はどこに投げるのか、そこらへんの解析すればmugen用デバッガも作れそうだけど
ここでは関係ないから詳細は放置、とりあえず整数値のオプション取得用関数っぽい

それを踏まえて後続の処理を見ると
↓のオプション2つ取得はrangeの値を取得で間違いなさそうで、それぞれediとesiに結果を入れている
0046F0B0 8D5618              	lea	edx, [esi+18h]
0046F0B3 6A01 push 01h
0046F0B5 52 push edx
0046F0B6 53 push ebx
0046F0B7 E82485F9FF call 004075E0h
0046F0BC 83C630 add esi, 30h
0046F0BF 6A01 push 01h
0046F0C1 56 push esi
0046F0C2 53 push ebx
0046F0C3 8BF8 mov edi, eax
0046F0C5 E81685F9FF call 004075E0h0046F0CA 8BF0 mov esi, eax


ということは、直後の↓の処理はesi > ediになるように
どちらのほうが小さいかをチェックして必要に応じて入れ替えていると
0046F0CF 3BF7                	cmp	esi, edi
0046F0D1 7D08 jge 0046F0DBh
0046F0D3 8BF7 mov esi, edi
0046F0D5 89442418 mov [esp+18h], eax
0046F0D9 8BF8 mov edi, eax


ここまで理解できれば、命令の意味を調べながらだけど
最初に0除算の原因解説した部分もすらすらっと読み解ける

ね、簡単でしょう?

コメントの投稿

非公開コメント

プロフィール

Author:drab
霊夢改変キャラ
「12 3 ! {V} [_]」
公開中
L霊夢でもl_reimuでも好きなように読んでください

最新記事
最新コメント
月別アーカイブ
カテゴリ
検索フォーム
RSSリンクの表示
リンク
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。