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

StackOverflow https://stackoverflow.com/questions/931257

質問

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

私は持っている IDA Pro 逆アセンブラ そしてその WinDbg デバッガー winmine.exe を両方にロードしました。地雷原を表すデータ構造の場所を見つけるという点で、これらのプログラムのいずれかについて実用的なヒントを誰かが提供してもらえますか?

WinDbgではブレークポイントを設定することができますが、どの時点でメモリのどの位置にブレークポイントを設定すればよいのか想像するのが難しいです。同様に、IDA Pro で静的コードを表示すると、地雷原を表す関数やデータ構造をどこから見つければよいのかわかりません。

Stackoverflow に私を正しい方向に導いてくれるリバース エンジニアはいますか?

役に立ちましたか?

解決

パート 1/3


リバース エンジニアリングに真剣に取り組んでいる場合は、トレーナーやチート エンジンのことは忘れてください。

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

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

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

F2アクセラレータコマンドを調べてみることにしました。

アクセラレータ処理コードを見つけるには、ウィンドウ メッセージ処理プロシージャ (WndProc) を見つける必要があります。これは、CreateWindowEx および RegisterClass 呼び出しによって追跡できます。

読むには:

IDA の [インポート] ウィンドウを開き、「CreateWindow*」を見つけてそこにジャンプし、「Jump xref to operand (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 値を見つける簡単な方法です。

アクセラレータの処理を理解するには、以下を確認してください。

1 つの回答に対してかなりの量のテキストが表示されます。ご興味がございましたら、さらにいくつかの記事を書くことができます。長い話を短くすると、バイトの配列 [24x36] として保存された地雷原です。0x0F はバイトが使用されていないことを示します (小さいフィールドを再生)。0x10 - 空のフィールド、0x80 - 地雷です。

パート 2/3


はい、F2 ボタンを押してみましょう。

によると キーボード アクセラレータの使用 F2ボタン押下時 wndProc関数

...wm_commandまたはwm_syscommandメッセージを受信します。WPARAMパラメーターの低次の単語には、アクセルの識別子が含まれています。

さて、WM_COMMAND が処理される場所はすでに見つかりましたが、対応する wParam パラメーター値を決定するにはどうすればよいでしょうか?ここが リソースハッカー が登場します。バイナリを入力すると、すべてが表示されます。私にとってはアクセラレータテーブルのようなものです。

代替テキスト 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

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

WinDBG を使用して実際の値を見てみましょう ('bp 01003696';休憩中です。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

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

なぜ 360 時間 = 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  ................................

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

他のヒント

あなたの元を分解しようとしているが、何をする必要が実行中のプログラムのメモリ空間を見ているされているように思えます。バイナリエディタ HXD のは、あなただけのことをすることができます機能があります。

あなたは、メモリ空間にいるしたら、

、それはボードとあなたの周りの混乱しながら、メモリのスナップショットを取っての問題です。ない何対変更内容を分離します。あなたは、データ構造は、六角メモリにどこにあるのハンドルを持って考えるとき、それはメモリ内にある間、それを編集してみた結果、ボードの変更かどうかを確認します。

あなたが望むプロセスは、ビデオゲームのための「トレーナー」を構築するとは違っていません。これらは通常、健康や弾薬などの値がメモリ内に住んで発見し、その場でそれらを変更することに基づいています。あなたがゲームのトレーナーを構築する方法についていくつかの良いチュートリアルを見つけることができる可能性があります。

このコード プロジェクトの記事をチェックしてください。あなたが言及したブログ投稿よりももう少し詳しく説明されています。

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

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

「Windbgでは、ブレークポイントを設定できますが、どのポイントでブレークポイントを設定するか、どのメモリの位置を設定するかを想像することは困難です。同様に、IDA Proで静的コードを表示すると、鉱山フィールドを表す関数またはデータストラクチャをどこから見つけ始めるかさえわかりません。」

その通り!

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

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

ところで、私は今掃海艇をスキャンしています オリーDbg.

アップデート: ニモ 素晴らしいツールを思い出しました、 チートエンジン エリック「ダークバイト」ハイネン著。

Cheat Engine (CE) は、他のプロセスのメモリ空間を監視および変更するための優れたツールです。それ以上 基本的な CE には、プロセスの逆アセンブルされたメモリの表示や他のプロセスへのコードの挿入などの特別な機能があります。

( 本物 このプロジェクトの価値は、ソース コード -Delphi- をダウンロードして、それらのメカニズムがどのように実装されているかを見ることができることです - 私は何年も前にそれをしました :o)

この非常にトピックに関するかなり良い記事は無知をで見つけることができます。それはかなり詳細に(エンジニアリングのWin32アプリケーションを逆に導入される)マインスイーパを逆にカバーし、すべてのかなり素晴らしいリソースの周りです。

次の Web サイトの方が役立つかもしれません。

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

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

  1. なんとなくソースコードを入手。
  2. 逆アセンブルして、残ったシンボルが役立つことを願っています。
  3. データ型を推測して操作し、メモリ スキャナーを使用して可能性を制限します。

報奨金に応じて

そうですね、もう一度読むと、リバース エンジニアリングの方法についてのよくある質問ではなく、WinDBG のようなデバッガの使用方法に関するガイドが欲しかったようです。検索する必要がある値を示す Web サイトはすでに紹介しましたが、問題は、その値をどのように検索するかです。

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

alt text

あなたが入力します

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

プレス "?" に続いて "s " をクリックするとヘルプが表示されます。

必要なメモリ パターンを見つけたら、alt+5 を押してメモリ ビューアを起動し、見やすく表示します。

alt text

WinDBG は慣れるまでに時間がかかりますが、他のデバッガと同じくらい優れています。

デバッガでトレースを開始するには良い点は、マウスのアップになります。だから、メインウィンドウプロシージャを見つける(私は窓のプロパティとイベントハンドラのアドレスを調べることができspyxxのようなツールは、そのうちの一つだと思います)。それに休憩して、マウスイベントを処理する場所を見つける - あなたが(WINDOWS.HでマウスアップのためのWM_XXXの値を見て)アセンブラでそれを認識することができた場合、スイッチがあるでしょう。

そこにブレークポイントを入れて、ステッピング始める。どこかの時間の間、あなたはvictumは、あなたが探しているデータ構造にアクセスする更新され、マウスのボタンと画面をリリースします。

任意の時点で行われているものを識別するためにしようとしていますが、あなたの現在の目的のために面白くないというの疑いがあるコードにあまりにも深く見て気にしない、我慢して。それを見極めるためにデバッガで数回かかることがあります。

通常のWin32アプリケーションのワークフローの知識があまりにも助けます。

鉱山は、おそらく2次元配列のいくつかの種類に格納されます。これは、ポインタの配列またはブール値の単一Cスタイルの配列のいずれかであることを意味します。

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

鉱山についての情報を仮定することはかなり合理的である(すなわち、それは2次元アレイ、またはアレイのサブアレイ'S)少なくとも行のメモリに連続レイアウトされます。このように、私は(つまり、1つのバイトは次の、最初のステップで変更、同じ行に複数の隣接するセルを開け、私が行くように、プロセスのメモリダンプを作成し、それらをdiffを試してみて、同じメモリ領域の任意の繰り返しの変更を探しますバイトは、次のステップなど)で同じ値を厳密に変更

があり、それがパックビット配列だという可能性は、(私のあたり3ビットがすべての可能な状態を記録するのに十分であるべき - オープン/閉鎖し、鉱山/無鉱山、非フラグを立て/フラグを立て)もありますので、私は外を見るだろうことは、あまりにも(難しい発見するのにパターンも、繰り返し可能になります)。しかし、それに対処するための便利な構造ではない、と私はメモリ使用量は、マインスイーパのボトルネックだったとは思わないので、この種のものが使用されることはほとんどありません。

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top