web-dev-qa-db-ja.com

マインスイーパの地雷レイアウトを表すデータ構造をメモリ内で見つけるにはどうすればよいですか?

マインスイーパをサンプルアプリケーションとして使用して、リバースエンジニアリングについて学習しようとしています。私はこれを発見しました MSDN記事 すべての地雷を明らかにする単純なWinDbgコマンドで、それは古く、詳細に説明されておらず、本当に私が探しているものではありません。

IDA Pro逆アセンブラーWinDbgデバッガー があり、両方にwinmine.exeをロードしました。誰かがこれらのプログラムのいずれかに対して、鉱山フィールドを表すデータ構造の場所を見つけるという点で実用的なヒントを提供できますか?

WinDbgではブレークポイントを設定できますが、ブレークポイントを設定するポイントとメモリの場所を想像するのは困難です。同様に、IDA Proで静的コードを表示するとき、鉱山フィールドを表す関数またはデータ構造の検索を開始する場所がわからない。

Stackoverflowで私を正しい方向に向けることができるリバースエンジニアはいますか?

92
KingNestor

パート1/3


リバースエンジニアリングを真剣に考えている場合は、トレーナーとチートエンジンを忘れてください。

優れたリバースエンジニアは、まずOS、コアAPI関数、プログラムの一般構造(実行ループ、Windows構造、イベント処理ルーチン)、ファイル形式(PE)を理解する必要があります。 Petzoldの古典的な「Windowsのプログラミング」は、オンラインMSDNだけでなく(www.Amazon.com/exec/obidos/ISBN=157231995X)にも役立ちます。

最初に、地雷原の初期化ルーチンをどこで呼び出すことができるかを考える必要があります。私は次のことを考えました:

  • ゲームを起動すると
  • 幸せそうな顔をクリックすると
  • ゲーム->新規をクリックするか、F2を押すと
  • レベルの難易度を変更するとき

F2アクセラレータコマンドをチェックアウトすることにしました。

アクセラレータ処理コードを見つけるには、ウィンドウメッセージ処理手順(WndProc)を見つけます。 CreateWindowExおよびRegisterClass呼び出しによって追跡できます。

読むには:

IDAの[インポート]ウィンドウを開き、[CreateWindow *]を見つけてそこにジャンプし、[Xrefをオペランド(X)にジャンプ]コマンドを使用して、呼び出される場所を確認します。呼び出しは1つだけにする必要があります。

上記のRegisterClass関数とそのパラメーターWndClass.lpfnWndProcを見てください。私の場合、すでに関数mainWndProcに名前を付けています。

.text:0100225D                 mov     [ebp+WndClass.lpfnWndProc], offset mainWndProc
.text:01002264                 mov     [ebp+WndClass.cbClsExtra], edi
.text:01002267                 mov     [ebp+WndClass.cbWndExtra], edi
.text:0100226A                 mov     [ebp+WndClass.hInstance], ecx
.text:0100226D                 mov     [ebp+WndClass.hIcon], eax

.text:01002292                 call    ds:RegisterClassW

関数名でEnterキーを押します(「N」を使用してより良い名前に変更します)

今見てみましょう

.text:01001BCF                 mov     edx, [ebp+Msg]

これはメッセージIDであり、F2ボタンを押す場合はWM_COMMAND値を含める必要があります。 111hと比較される場所を見つける必要があります。これは、IDAでedxをトレースダウンするか、WinDbgで 条件付きブレークポイントを設定 でゲームでF2を押すことで実行できます。

どちらの方法でも次のようなものにつながります

.text:01001D5B                 sub     eax, 111h
.text:01001D60                 jz      short loc_1001DBC

111hを右クリックし、「シンボリック定数」->「標準シンボリック定数を使用」を使用し、WM_と入力してEnterキーを押します。あなたは今持っているはずです

.text:01001D5B                 sub     eax, WM_COMMAND
.text:01001D60                 jz      short loc_1001DBC

これは、メッセージID値を見つける簡単な方法です。

アクセラレータの取り扱いを理解するには、チェックアウト:

単一の答えにはかなりの量のテキストがあります。興味があれば、別の投稿を書くことができます。長いストーリーの短い地雷原はバイトの配列[24x36]として格納され、0x0Fはバイトが使用されていないことを示します(より小さいフィールドを再生)、0x10-空のフィールド、0x80-鉱山。

パート2/3


OK、F2ボタンで続けましょう。

キーボードアクセラレータの使用 によると、F2ボタンが押されたときwndProc関数

... WM_COMMANDまたはWM_SYSCOMMANDメッセージを受信します。 wParamパラメーターの下位ワードには、アクセラレーターの識別子が含まれています。

OK、WM_COMMANDが処理される場所は既に見つかりましたが、対応するwParamパラメーター値を決定する方法は?これが Resource hacker の出番です。バイナリでフィードすると、すべてが表示されます。私にとってアクセラレータテーブルのようなものです。

alt text http://files.getdropbox.com/u/1478671/2009-07-29_161532.jpg

ここでわかるように、F2ボタンはwParamの510に対応しています。

それでは、WM_COMMANDを処理するコードに戻りましょう。 wParamと異なる定数を比較します。

.text:01001DBC HandleWM_COMMAND:                       ; CODE XREF: mainWndProc+197j
.text:01001DBC                 movzx   eax, Word ptr [ebp+wParam]
.text:01001DC0                 mov     ecx, 210h
.text:01001DC5                 cmp     eax, ecx
.text:01001DC7                 jg      loc_1001EDC
.text:01001DC7
.text:01001DCD                 jz      loc_1001ED2
.text:01001DCD
.text:01001DD3                 cmp     eax, 1FEh
.text:01001DD8                 jz      loc_1001EC8

コンテキストメニューまたは「H」キーボードショートカットを使用して10進数値を表示すると、ジャンプを確認できます

.text:01001DBC HandleWM_COMMAND:                       ; CODE XREF: mainWndProc+197j
.text:01001DBC                 movzx   eax, Word ptr [ebp+wParam]
.text:01001DC0                 mov     ecx, 528
.text:01001DC5                 cmp     eax, ecx
.text:01001DC7                 jg      loc_1001EDC
.text:01001DC7
.text:01001DCD                 jz      loc_1001ED2
.text:01001DCD
.text:01001DD3                 cmp     eax, 510
.text:01001DD8                 jz      loc_1001EC8 ; here is our jump

いくつかのprocを呼び出してwndProcを終了するコードチャンクにつながります。

.text:01001EC8 loc_1001EC8:                            ; CODE XREF: mainWndProc+20Fj
.text:01001EC8                 call    sub_100367A     ; startNewGame ?
.text:01001EC8
.text:01001ECD                 jmp     callDefAndExit  ; default

それは新しいゲームを開始する機能ですか?最後の部分でそれを見つけてください!乞うご期待。

パート3/3

その関数の最初の部分を見てみましょう

.text:0100367A sub_100367A     proc near               ; CODE XREF: sub_100140C+CAp
.text:0100367A                                         ; sub_1001B49+33j ...
.text:0100367A                 mov     eax, dword_10056AC
.text:0100367F                 mov     ecx, uValue
.text:01003685                 Push    ebx
.text:01003686                 Push    esi
.text:01003687                 Push    edi
.text:01003688                 xor     edi, edi
.text:0100368A                 cmp     eax, dword_1005334
.text:01003690                 mov     dword_1005164, edi
.text:01003696                 jnz     short loc_10036A4
.text:01003696
.text:01003698                 cmp     ecx, dword_1005338
.text:0100369E                 jnz     short loc_10036A4

2つの値(dword_10056AC、uValue)がレジスタeaxおよびecxに読み込まれ、別の2つの値(dword_1005164、dword_1005338)と比較されます。

WinDBG( 'bp 01003696'; on break 'p eax; p ecx')を使用して実際の値を見てください-私にとって地雷原の次元のように見えました。カスタム地雷原サイズで遊んでみると、最初のペアは新しい次元であり、2番目は現在の次元であることが示されました。新しい名前を設定しましょう。

.text:0100367A startNewGame    proc near               ; CODE XREF: handleButtonPress+CAp
.text:0100367A                                         ; sub_1001B49+33j ...
.text:0100367A                 mov     eax, newMineFieldWidth
.text:0100367F                 mov     ecx, newMineFieldHeight
.text:01003685                 Push    ebx
.text:01003686                 Push    esi
.text:01003687                 Push    edi
.text:01003688                 xor     edi, edi
.text:0100368A                 cmp     eax, currentMineFieldWidth
.text:01003690                 mov     dword_1005164, edi
.text:01003696                 jnz     short loc_10036A4
.text:01003696
.text:01003698                 cmp     ecx, currentMineFieldHeight
.text:0100369E                 jnz     short loc_10036A4

少し後で新しい値が現在の値を上書きし、サブルーチンが呼び出されます

.text:010036A7                 mov     currentMineFieldWidth, eax
.text:010036AC                 mov     currentMineFieldHeight, ecx
.text:010036B2                 call    sub_1002ED5

そして私がそれを見たとき

.text:01002ED5 sub_1002ED5     proc near               ; CODE XREF: sub_1002B14:loc_1002B1Ep
.text:01002ED5                                         ; sub_100367A+38p
.text:01002ED5                 mov     eax, 360h
.text:01002ED5
.text:01002EDA
.text:01002EDA loc_1002EDA:                            ; CODE XREF: sub_1002ED5+Dj
.text:01002EDA                 dec     eax
.text:01002EDB                 mov     byte ptr dword_1005340[eax], 0Fh
.text:01002EE2                 jnz     short loc_1002EDA

私は地雷原アレイを見つけたと完全に確信していました。 360xバイトの長さの配列(dword_1005340)を0xFで初期化するサイクルの原因。

なぜ360h = 864ですか?行には32バイトが必要で、864は32で割ることができるといういくつかのキューがあります。したがって、配列は27 * 32セルを保持できます(UIは最大24 * 30フィールドを許可しますが、境界の配列の周りに1バイトのパディングがあります)。

次のコードは、地雷原の上部と下部の境界線(0x10バイト)を生成します。その混乱の中でループの繰り返しを見ることができることを願っています;)私は紙とペンを使わなければなりませんでした

.text:01002EE4                 mov     ecx, currentMineFieldWidth
.text:01002EEA                 mov     edx, currentMineFieldHeight
.text:01002EF0                 lea     eax, [ecx+2]
.text:01002EF3                 test    eax, eax
.text:01002EF5                 Push    esi
.text:01002EF6                 jz      short loc_1002F11    ; 
.text:01002EF6
.text:01002EF8                 mov     esi, edx
.text:01002EFA                 shl     esi, 5
.text:01002EFD                 lea     esi, dword_1005360[esi]
.text:01002EFD
.text:01002F03 draws top and bottom borders
.text:01002F03 
.text:01002F03 loc_1002F03:                            ; CODE XREF: sub_1002ED5+3Aj
.text:01002F03                 dec     eax
.text:01002F04                 mov     byte ptr MineField?[eax], 10h ; top border
.text:01002F0B                 mov     byte ptr [esi+eax], 10h       ; bottom border
.text:01002F0F                 jnz     short loc_1002F03
.text:01002F0F
.text:01002F11
.text:01002F11 loc_1002F11:                            ; CODE XREF: sub_1002ED5+21j
.text:01002F11                 lea     esi, [edx+2]
.text:01002F14                 test    esi, esi
.text:01002F16                 jz      short loc_1002F39

そして、残りのサブルーチンは左右の境界線を描画します

.text:01002F18                 mov     eax, esi
.text:01002F1A                 shl     eax, 5
.text:01002F1D                 lea     edx, MineField?[eax]
.text:01002F23                 lea     eax, (MineField?+1)[eax+ecx]
.text:01002F23
.text:01002F2A
.text:01002F2A loc_1002F2A:                            ; CODE XREF: sub_1002ED5+62j
.text:01002F2A                 sub     edx, 20h
.text:01002F2D                 sub     eax, 20h
.text:01002F30                 dec     esi
.text:01002F31                 mov     byte ptr [edx], 10h
.text:01002F34                 mov     byte ptr [eax], 10h
.text:01002F37                 jnz     short loc_1002F2A
.text:01002F37
.text:01002F39
.text:01002F39 loc_1002F39:                            ; CODE XREF: sub_1002ED5+41j
.text:01002F39                 pop     esi
.text:01002F3A                 retn

WinDBGコマンドをスマートに使用すると、クールな地雷原ダンプ(カスタムサイズ9x9)を提供できます。国境をチェックしてください!

0:000> db /c 20 01005340 L360
01005340  10 10 10 10 10 10 10 10-10 10 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
01005360  10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
01005380  10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
010053a0  10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
010053c0  10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
010053e0  10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
01005400  10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
01005420  10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
01005440  10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
01005460  10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
01005480  10 10 10 10 10 10 10 10-10 10 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
010054a0  0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
010054c0  0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................
010054e0  0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f  ................................

うーん、トピックを閉じるには別の投稿が必要なようです

122
Stanislav

ソースを逆アセンブルしようとしているように見えますが、実行する必要があるのは、実行中のプログラムのメモリ空間を調べることです。 16進エディタ HxD には、まさにそれを可能にする機能があります。

http://www.freeimagehosting.net/uploads/fcc1991162.png

メモリ空間に入ったら、ボードをいじりながらメモリのスナップショットを撮るだけです。変化するものとそうでないものを分離します。 16進メモリ内のデータ構造の位置を把握していると思われる場合は、メモリ内で編集してみて、結果としてボードが変化するかどうかを確認してください。

あなたが望むプロセスは、ビデオゲームの「トレーナー」を構築するのと同じです。これらは通常、健康や弾薬などの値がメモリ内のどこに存在するかを見つけ、その場で変更することに基づいています。ゲームトレーナーの作成方法に関する優れたチュートリアルを見つけることができる場合があります。

15
James McMahon

このコードプロジェクトの記事をご覧ください。これは、あなたが言及したブログ投稿よりも少し深いものです。

http://www.codeproject.com/KB/trace/minememoryreader.aspx

編集

この記事は、掃海艇に関するものではありませんが、WinDbgを使用してメモリを探索するためのステップバイステップガイドを提供します。

http://www.codingthewheel.com/archives/extracting-hidden-text-with-windbg

編集2

繰り返しになりますが、これは掃海艇に関するものではありませんが、これは間違いなく私のメモリデバッグのための思考の糧を与えてくれました。ここには豊富なチュートリアルがあります。

http://memoryhacking.com/forums/index.php

また、 CheatEngine (Nick D.による言及)をダウンロードし、付属のチュートリアルを実行してください。

11
Kirschstein

「WinDbgではブレークポイントを設定できますが、ブレークポイントを設定するポイントとメモリの場所を想像するのは困難です。同様に、IDA Proで静的コードを表示するとき、どこから始めてもわからない地雷原を表す関数またはデータ構造を見つけます。」

丁度!

まあ、あなたは鉱山テーブルの構築中に呼び出されるrandom()のようなルーチンを探すことができます。この book は、リバースエンジニアリングを実験していたときに非常に役立ちました。 :)

一般に、ブレークポイントを設定するのに適した場所は、メッセージボックスの呼び出し、サウンドを再生する呼び出し、タイマー、およびその他のwin32 APIルーチンです。

ところで、私は今掃海艇を OllyDbg でスキャンしています。

更新:nemo 素晴らしいツールを思い出した Cheat Engine by Eric "Dark Byte" Heijnen 。

チートエンジン(CE)は、他のプロセスのメモリスペースを監視および変更するための優れたツールです。そのbasic機能以外に、CEには、プロセスの逆アセンブルされたメモリを表示したり、他のプロセスにコードを挿入したりするなどの特別な機能があります。

(そのプロジェクトのreal値は、ソースコード-Delphi-をダウンロードして、それらのメカニズムがどのように実装されているかを確認できることです。 o)

9

このトピックに関する非常に良い記事は ninformed にあります。これは、マインスイーパの反転(Win32アプリのリバースエンジニアリングの概要として)を非常に詳細に説明しており、非常に優れたリソースです。

5
mrduclaw

このWebサイトの方が役立つ場合があります。

http://www.subversity.net/reversing/hacking-minesweeper

これを行う一般的な方法は次のとおりです。

  1. どういうわけかソースコードを入手してください。
  2. 分解して残りのシンボルが役立つことを願っています。
  3. データ型を推測して操作し、メモリスキャナを使用して可能性を制限してください。

バウンティに応えて

さて、2回目の読書では、リバースエンジニアリングの方法に関する通常の質問ではなく、WinDBGのようなデバッガの使用方法に関するガイドが必要であるかのように見えます。検索する必要がある値を示すWebサイトを既に示しましたので、質問はどのように検索するのですか?

Minesweeperがインストールされていないため、この例ではメモ帳を使用しています。しかし、考え方は同じです。

alt text

入力する

s <options> <memory start> <memory end> <pattern>

「?」を押してから「s」を押すと、ヘルプが表示されます。

必要なメモリパターンが見つかったら、Alt + 5を押して、メモリビューアを起動してNiceディスプレイを表示できます。

alt text

WinDBGはある程度慣れる必要がありますが、他のデバッガーと同様に優れています。

4
Unknown

鉱山に関する情報は、少なくとも行(つまり、2D配列または配列の配列)のメモリに連続して配置されると想定するのはかなり合理的です。したがって、同じ行のいくつかの隣接するセルを開いて、プロセスのメモリダンプを作成し、それらを比較して、同じメモリ領域で繰り返し変更を探します(つまり、最初のステップで1バイトが変更され、次のステップでバイトがまったく同じ値に変更されたなど)。

また、それがパックされたビット配列である可能性もあります(鉱山ごとに3ビットで、すべての可能な状態を記録するのに十分でなければなりません-クローズ/オープン、鉱山/非鉱山、フラグ付き/フラグなし)パターンは再現可能ですが、見つけるのは難しくなります)。しかし、それは対処するのに便利な構造ではなく、マインスイーパのメモリ使用量がボトルネックだとは思わないので、この種のものが使用される可能性は低いです。

0
Pavel Minaev

地雷は、おそらく何らかの二次元配列に格納されます。これは、ポインターの配列または単一のCスタイルのブール配列のいずれかであることを意味します。

フォームがマウスアップイベントを受け取るたびに、このデータ構造が参照されます。インデックスは、おそらく整数除算を使用して、マウス座標を使用して計算されます。これはおそらく、オペランドの1つがオフセットとcmpを使用して計算されるxまたは同様の命令を探す必要があることを意味します。ここで、xは整数除算を含む計算。オフセットは、データ構造の先頭へのポインターになります。

0
Jørgen Fogh

デバッガーでトレースを開始する適切なポイントは、マウスを上げたときです。メインウィンドウプロシージャを見つけます(spyxxなどのツールはウィンドウのプロパティを検査でき、イベントハンドラーアドレスもその1つです)。それに割り込んで、マウスイベントを処理する場所を見つけます。アセンブラーで認識できる場合は、スイッチがあります(windows.hでマウスを上げるためのWM_XXXの値を見てください)。

ブレークポイントをそこに置き、ステップインを開始します。マウスボタンを放してから画面が更新されるまでの間に、victumは探しているデータ構造にアクセスします。

辛抱強く、いつでも何が行われているのかを特定しようとしますが、現在の目的に興味がないと思われるコードを深く見すぎないでください。デバッガーで数回実行して特定することがあります。

通常のwin32アプリケーションワークフローの知識も役立ちます。

0
Eugene

厳密には「リバースエンジニアのツール」ではなく、私のようなバカでさえ使用できるおもちゃの詳細ですが、 チートエンジン を確認してください。これにより、メモリのどの部分がいつ変更されたかを簡単に追跡できます。また、ポインタを使用して、変更されたメモリ部分を追跡することもできます(おそらく必要ありませんが)。素敵なインタラクティブなチュートリアルが含まれています。

0
Ivan Vučica