Hatena::ブログ(Diary)

oupoの日記

2017-12-09

乱数調整で遊ぼう 別解

06:05

ろいしんさんの乱数調整で遊ぼうを中身を見ながら解析します。

1. 文字列を調べる

飛ばしてもよいです。バイナリエディタでは2バイトアラインされていないところの2バイト文字が見れなかったりするので念のため。

以下のRubyスクリプトで文字列を出力します

fname = ARGV[0]
File.binread(fname).scan(/(?:[\x81-\x9f\xe0-\xfc][\x40-\x7e\x80-\xfc]|[\x20-\x7e]){4,}/n).each do |matched|
  puts matched.encode("utf-8", "cp932", undef: :replace)
end

結果

おいしいみず
サイコソーダ
ミックスオレ
何をしますか?
おかね:%d円
しらべる -> 0
ポケモン -> 1
かいもの -> 2
________
| || ⊂⊃⊂⊃ ||
| || [][][][] ||
| ||
| || [][][][] ||
| || poke
 Θ ||
| || 口口口口 ||
|| ==== ||
    ̄ ̄ ̄ ̄ ̄ ̄
自動販売機が ある!
ほしい 飲み物は....
1. おいしいみず 200円
2. サイコソーダ 350円
3. ミックスオレ 400円
0. やめる
 おかね:%d円
...やっぱり やめた
YOU LOSE...
ペラップを しばいた!
もう一度遊びますか?
はい -> 1
いいえ -> 0
おかねが 足りない....
ガコン!
%sが でてきた!
当たりだ! もう一本
%sが でてきた!
YOU WIN!!
ベトベター
オニドリル
マグマッグ
ベトベトン
がんばりや
さみしがり
ゆうかん 
いじっぱり
やんちゃ 
ずぶとい 
すなお  
のんき  
わんぱく 
のうてんき
おくびょう
せっかち 
まじめ  
ようき  
むじゃき 
ひかえめ 
おっとり 
れいせい 
てれや  
うっかりや
おだやか 
おとなしい
なまいき 
しんちょう
きまぐれ 
するどいくちばし
きんのたま
モンスターボールを 買いました
おかね:%d円
野生の%sを つかまえた!
%s Lv.%d
性格:%s
個体値:%d-%d-%d-%d-%d-%d
なんと!
つかまえた%sが %sをもっていた!
%sを 売って
%d円 手に入れた
おかね:%d円
YOU LOSE...

2. とりあえず普通に遊ぶ

遊びます

3. IDAで逆アセンブルする

IDAで逆アセンブルします。

f:id:oupo:20171209053737p:image

右下に文字列一覧があります。

試しに「当たりだ! もう一本」をクリックしましょう

f:id:oupo:20171209054049p:image

「DATA XREF DATA XREF: sub_4011D0+9F↑o」というのがありますね。

これを使うと、この文字列を参照しているコードに移動できます。大変べんり。

f:id:oupo:20171209054050p:image

無事、乱数を使ってあたりが出るかどうか判定しているところが見れました。

疑似乱数はx_{n+1} = (x_n * 41C64E6Dh + 6073h) % 2^32のようで、seedはdword_404374に格納されているようです。

初期seed決定も調べておきましょう。

dword_404374を選んで「Jump to xref to operand」を押すと、seedを読み書きしている部分が表示されます。

f:id:oupo:20171209054051p:image

見つかりました。

これを見るとtime64()の戻り値 (1970年1月1日 00:00:00からの経過秒数)の下32bitを使っているようです。

ゲームの12/8からのバージョンではペラップが実際に音を鳴らすようになりました。

これを調べてみます。

Beepという文字列があるのでBeep() APIを使って音を鳴らしているのだと推定されます。呼んでいるところを見てみましょう。

f:id:oupo:20171209054054p:image

(((seed >> 16) * 8192) >> 16) * 110 / 8192 + 440

によって周波数を定めているようです。

4. seed書き込みトレースしながら実行

seed書き込みがあったらそのことを出力しながら実行してみます。

メニュー > Debugger > Breakpoints > Breakpoint listを開きます。

f:id:oupo:20171209120128p:image

このように設定します。

そしてメニュー > Debugger > Start Processを押してみましょう。プログラムが実行されます。

このとき遊んでみると、seedが更新されるたびにTrace Windowにそのことが出力されます。

f:id:oupo:20171209120434p:image

初期seedは0x5a2b5249であることがわかります。Rubyで確認してみると

irb(main):026:0> (Time.utc(1970,1,1) + 0x5a2b5249).getlocal
=> 2017-12-09 12:02:33 +0900

となって確かに起動時刻が使われています。

ペラップを鳴かせると1消費したり野生のポケモンを捕まえるといくつか消費したり、といったこともトレースされます。

長くなってきたのでこの辺りで。

これ以外の情報が気になる方はぜひ自分で解析してみてください。

トラックバック - http://d.hatena.ne.jp/oupo/20171209/1512767116
Connection: close