如何在内存中找到代表扫雷布局的数据结构?

发布于 2024-07-22 20:35:47 字数 661 浏览 11 评论 0原文

我正在尝试使用扫雷作为示例应用程序来了解逆向工程。 我找到了这篇 MSDN 文章 一个简单的 WinDbg 命令,它显示了所有的地雷,但它很旧,没有任何详细解释,而且确实不是我正在寻找的。

我有 IDA Pro 反汇编程序WinDbg 调试器 并且我已将 winmine.exe 加载到它们中。 有人可以为这两个程序中的任何一个提供一些实用的技巧来查找代表雷区的数据结构的位置吗?

在WinDbg中我可以设置断点,但是我很难想象在什么点以及在什么内存位置设置断点。 同样,当我在 IDA Pro 中查看静态代码时,我什至不知道从哪里开始找到代表雷区的函数或数据结构。

Stackoverflow 上有逆向工程师可以为我指明正确的方向吗?

I'm trying to learn about reverse engineering, using Minesweeper as a sample application. I've found this MSDN article on a simple WinDbg command that reveals all the mines but it is old, is not explained in any detail and really isn't what I'm looking for.

I have IDA Pro disassembler and the WinDbg debugger and I've loaded winmine.exe into both of them. Can someone provide some practical tips for either of these programs in terms of finding the location of the data structure that represents the mine field?

In WinDbg I can set breakpoints, but it is difficult for me to imagine at what point to set a breakpoint and at what memory location. Similarly, when I view the static code in IDA Pro, I'm not sure where to even begin to find the function or data structure that represents the mine field.

Are there any Reverse Engineers on Stackoverflow that can point me in the right direction?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

回眸一笑 2024-07-29 20:35:47

第 1 部分(共 3 部分)

如果您热衷于逆向工程,请忘记训练器和作弊引擎。

好的逆向工程师首先应该了解操作系统、核心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)”命令查看它被调用的位置。 应该只有一个电话。

现在查看上面的 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 按钮的情况下,该 ID 应包含 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 部分)

好的,让我们继续使用 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”键盘快捷键显示十进制值,您可以看到我们的跳转,

.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

它导致调用某些过程并退出 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

有两个值(dword_10056AC、uValue)被读入寄存器 eax 和 ecx 中,并与另外两个值(dword_1005164、dword_1005338)进行比较。

使用 WinDBG 查看实际值('bp 01003696';在中断时'p eax; p ecx') - 对我来说它们似乎是雷区尺寸。 使用自定义雷区大小表明第一对是新尺寸,第二对是当前尺寸。 让我们设置新名称。

.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

我完全确定我找到了雷区数组。 使用 0xF 初始化 360h 字节长度数组 (dword_1005340) 的循环原因。

为什么360小时=864? 下面有一些提示,行占用 32 个字节,864 可以除以 32,因此数组可以容纳 27*32 个单元格(尽管 UI 允许最大 24*30 字段,但数组周围有一个字节填充作为边框)。

以下代码生成雷区顶部和底部边框(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  ................................

嗯,看来我需要另一篇文章来结束这个话题

Part 1 of 3

If you are serious into reverse engineering - forget about trainers and cheat engines.

Good reverse engineer should first get to know OS, core API functions, program general structure (what is run loop, windows structures, event handling routines), file format (PE). Petzold's classics "Programming Windows" can help (www.amazon.com/exec/obidos/ISBN=157231995X) as well as online MSDN.

First you should think about where minefield initialization routine can be called. I thought of following:

  • When you launch the game
  • When you click happy face
  • When you click Game->New or press F2
  • When you change level difficulty

I decided to check out F2 accelerator command.

To find accelerator handling code you are to find window message handling procedure (WndProc). It can be traced down by CreateWindowEx and RegisterClass calls.

To read:

Open up IDA, Imports window, find "CreateWindow*", jump to it and use "Jump xref to operand (X)" command to see where it is called. There should be just one call.

Now look above for RegisterClass function and it's parameter WndClass.lpfnWndProc. I already named function mainWndProc in my case.

.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

Hit Enter on function name (use 'N' to rename it to something better)

Now take a look at

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

This is message id, which in case of F2 button press should contain WM_COMMAND value. You are to find where it is compared to 111h. It can be done either by tracing down edx in IDA or by setting conditional breakpoint in WinDbg and pressing F2 in the game.

Either way leads to something like

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

Right click on 111h and use "Symbolic constant" -> "Use standard symbolic constant", type WM_ and Enter. You should now have

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

It is an easy way to find out message id values.

To understand accelerator handling check out:

It's quite a lot of text for a single answer. If you are interested I can write another couple of posts. Long story short minefield stored as an array of bytes [24x36], 0x0F shows that byte is not used (playing smaller field), 0x10 - empty field, 0x80 - mine.

Part 2 of 3

Ok, let's go on with F2 button.

According to Using Keyboard Accelerators when F2 button is pressed wndProc function

... receives a WM_COMMAND or WM_SYSCOMMAND
message. The low-order word of the
wParam parameter contains the
identifier of the accelerator.

Ok, we already found where WM_COMMAND is processed, but how to determine corresponding wParam parameter value? This is where Resource hacker comes into play. Feed it with binary and it shows you everything. Like accelerators table for me.

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

You can see here, that F2 button corresponds to 510 in wParam.

Now let's get back to code, that handles WM_COMMAND. It compares wParam with different constants.

.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

Use context menu or 'H' keyboard shortcut to display decimal values and you can see our jump

.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

It leads to code chunk that calls some proc and exits wndProc.

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

Is that the function that initiates new game? Find that out in the last part! Stay tuned.

Part 3 of 3

Let's take a look at the first part of that function

.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

There are two values (dword_10056AC, uValue) read into registers eax and ecx and compared to another two values (dword_1005164, dword_1005338).

Take a look at actual values using WinDBG ('bp 01003696'; on break 'p eax; p ecx') - they seemed like minefield dimensions for me. Playing with custom minefield size showed that first pair are new dimensions and second - current dimensions. Let's set new names.

.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

A little bit later new values overwrite current and subroutine is called

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

And when I saw it

.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

I was completely sure that I found minefield array. Cause of cycle which inits 360h bytes length array (dword_1005340 ) with 0xF.

Why 360h = 864? There are some cues below that row takes 32 bytes and 864 can be divided by 32, so array can hold 27*32 cells (although UI allows max 24*30 field, there is one byte padding around array for borders).

Following code generates minefield top and bottom borders (0x10 byte). I hope you can see loop iteration in that mess ;) I had to use paper and pen

.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

And the rest of subroutine draws left and right borders

.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

Smart usage of WinDBG commands can provide you cool minefield dump (custom size 9x9). Check out the borders!

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  ................................

Hmm, looks like I'll need another post to close the topic

眉目亦如画i 2024-07-29 20:35:47

看起来您正在尝试反汇编源代码,但您需要做的是查看正在运行的程序的内存空间。 十六进制编辑器 HxD 有一个功能可以让您做到这一点。

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

一旦进入内存空间,就需要在摆弄主板时拍摄内存快照。 隔离发生变化的内容与未发生变化的内容。 当您认为自己掌握了数据结构在十六进制内存中的位置时,请尝试在内存中对其进行编辑,并查看棋盘是否会因此而发生变化。

您想要的过程与为视频游戏构建“训练器”没有什么不同。 这些通常是基于查找生命值和弹药等值在内存中的位置并动态更改它们。 您也许可以找到一些关于如何构建游戏训练器的好教程。

It seems like you are trying to disassemble the source but what you need to do is look at the memory space of the running program. The hex editor HxD has a feature that lets you just that.

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

Once you're in the memory space, it is a matter of taking snapshots of the memory while you mess around with the board. Isolate what changes versus what doesn't. When you think you have a handle on where the data structure lies in hex memory, try editing it while it is in memory and see if the board changes as a result.

The process you want is not unlike building a 'trainer' for a video game. Those are usually based on finding where values like health and ammo live in memory and changing them on the fly. You may be able to find some good tutorials on how to build game trainers.

岛歌少女 2024-07-29 20:35:47

查看这篇代码项目文章,它比您提到的博客文章更深入一些。

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. 提到)并完成附带教程。

Check out this code project article, it's a little more in depth than the blog post you mentioned.

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

Edit

And this article, although not about minesweeper directly, gives you a good step by step guide on hunting through memory using WinDbg:

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

Edit 2

Again, this isn't about minesweeper, but it has definitely given me some food for thought for my memory debugging, there's a wealth of tutorials here:

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

Also, download CheatEngine (mentioned by Nick D.) and work through the tutorial it comes with.

﹏半生如梦愿梦如真 2024-07-29 20:35:47

“在WinDbg中我可以设置断点,但是
我很难想象
在什么点设置断点
什么内存位置。 同样,当
我在IDA Pro中查看静态代码,我是
不知道从哪里开始寻找
函数或数据结构
代表雷区。”

没错!

那么,您可以查找像 random() 这样的例程,这些例程将在构建雷表期间调用。这个 book 在我尝试逆向工程时给了我很多帮助。:)

一般来说,设置断点的好地方是调用消息框、调用播放声音、计时器和其他 win32 API 例程。

顺便说一句,我现在正在使用 OllyDbg 扫描扫雷。

更新: nemo 让我想起了一个很棒的工具,作弊引擎 作者:Eric“Dark Byte”Heijnen。

Cheat Engine (CE) 是一个用于监视和修改其他进程内存空间的好工具。 除了这个基本功能之外,CE 还有更多特殊功能,例如查看进程的反汇编内存以及将代码注入其他进程。

(该项目的真正价值在于您可以下载源代码 - Delphi - 并查看这些机制是如何实现的 - 我很多年前就这样做了 :o)

"In WinDbg I can set breakpoints, but
it is difficult for me to imagine at
what point to set a breakpoint and at
what memory location. Similarly, when
I view the static code in IDA Pro, I'm
not sure where to even begin to find
the function or datastructure that
represents the mine field."

Exactly!

Well, you can look for routines like random() that will be called during the construction of the mines table. This book helped me a lot when I was experimenting with reverse engineering. :)

In general, good places for setting break points are calls to message boxes, calls to play a sound, timers and other win32 API routines.

BTW, I am scanning minesweeper right now with OllyDbg.

Update: nemo reminded me a great tool, Cheat Engine by Eric "Dark Byte" Heijnen.

Cheat Engine (CE) is a great tool for watching and modifying other processes memory space. Beyond that basic facility, CE has more special features like viewing the disassembled memory of a process and injecting code into other processes.

(the real value of that project is that you can download the source code -Delphi- and see how those mechanisms were implemented - I did that many years ago :o)

怀里藏娇 2024-07-29 20:35:47

假设有关地雷的信息至少按行连续布置在内存中是相当合理的(即,它是二维数组或数组的数组)。 因此,我会尝试打开同一行中的几个相邻单元,在进行过程中进行内存转储,然后比较它们并查找同一内存区域中的任何重复更改(即第一步更改了 1 个字节,下一步更改了 1 个字节)。字节在下一步中更改为完全相同的值,等等)。

也有可能它是一个打包的位数组(每个矿井 3 位应该足以记录所有可能的状态 - 关闭/打开、我的/无我的、标记/非标记),所以我也会留意这一点(这些模式也是可重复的,尽管更难发现)。 但这不是一个方便处理的结构,而且我不认为内存使用是扫雷的瓶颈,所以不太可能使用这种东西。

It is fairly reasonable to assume that information about mines is layed out contiguously in memory at least for rows (i.e. it's a 2D-array, or an array-of-arrays). Thus, I would try opening several adjacent cells in the same row, making memory dumps of the process as I go, and then diff them and look for any repeating changes in the same memory region (i.e. 1 byte changed on first step, the next byte changed to exact same value on the next step, etc).

There's also possibility that it's a packed bit array (3 bits per mine should be sufficient to record all possible states - closed/open, mine/no-mine, flagged/non-flagged), so I'd look out for that too (the patterns would also be repeatable, though harder to spot). But it's not a convenient structure to deal with, and I don't think memory usage was a bottleneck for Minesweeper, so it is unlikely that this sort of thing would be used.

思念绕指尖 2024-07-29 20:35:47

关于这个主题的一篇非常好的文章可以在 Uninformed 找到。 它非常详细地介绍了逆向扫雷(作为逆向工程 Win32 应用程序的介绍),并且是一个非常好的资源。

A pretty good article on this very topic can be found at Uninformed. It covers reversing Minesweeper (as an introduction to reverse engineering Win32 apps) in pretty great detail and is all around a pretty great resource.

無處可尋 2024-07-29 20:35:47

这个网站可能会更有帮助:

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

执行此操作的一般方法是:

  1. 以某种方式获取源代码。
  2. 拆解希望剩下的符号能帮到你。
  3. 猜测数据类型并尝试操作它并使用内存扫描器来限制可能性。

响应赏金

好吧,在第二次阅读时,您似乎想要一个有关如何使用像 WinDBG 这样的调试器的指南,而不是如何进行逆向工程的常见问题。 我已经向您展示了一个网站,该网站告诉您需要搜索的值,所以问题是,您如何搜索它?

我在本例中使用记事本,因为我没有安装扫雷。 但想法是一样的。

alt text

您键入

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

按“?”,然后按“s”查看帮助。

找到所需的内存模式后,您可以按 alt+5 调出内存查看器以进行良好的显示。

alt text

WinDBG 需要一些时间来适应,但它与任何其他调试器一样好。

This website might be more helpful:

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

The general way to go about doing this is:

  1. Somehow get source code.
  2. Disassemble and hope leftover symbols can help you.
  3. Guess the data type and try to manipulate it and use a memory scanner to limit the possibilities.

In response to Bounty

Well, on a second reading, it appears as though you wanted a guide on how to use a debugger like WinDBG rather than the usual question of how to reverse engineer. I've already shown you the website that tells you the values you need to search for, so the question is, how do you search for it?

I am using Notepad in this example because I do not have Minesweeper installed. But the idea is the same.

alt text

You type

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

Press "? " and then "s " to see the help.

Once you've found the memory pattern you want, you can then press alt+5 to bring up the memory viewer for a nice display.

alt text

WinDBG takes some getting used to, but it is as good as any other debugger out there.

尾戒 2024-07-29 20:35:47

地雷可能会存储在某种二维数组中。 这意味着它要么是一个指针数组,要么是一个 C 风格的布尔数组。

每当表单接收到鼠标释放事件时,就会引用此数据结构。 索引将使用鼠标坐标计算,可能使用整数除法。 这意味着您可能应该寻找 cmp 或类似的指令,其中操作数之一是使用偏移量和 x 计算的,其中 x > 是涉及整数除法的计算结果。 偏移量将是指向数据结构开头的指针。

The mines will probably be stored in some kind of two-dimensional array. This means that it is either an array of pointers or a single C style array of booleans.

Whenever the form receives a mouse-up event this data structure is referenced. The index will be calculated using the mouse coordinate, probably using integer division. That means that you should probably look for a cmp or a similar instruction, where one of the operands is computed using an offset and x, where x is the result of a calculation involving integer division. The offset will then be the pointer to the beginning of the data structure.

执手闯天涯 2024-07-29 20:35:47

在调试器中开始跟踪的一个好点是鼠标松开。 所以找到主窗口过程(我认为像spyxx这样的工具可以检查窗口属性,事件处理程序地址就是其中之一)。 闯入它并找到它处理鼠标事件的位置 - 如果您可以在汇编程序中识别它,就会有一个开关(查看 windows.h 中鼠标向上的 WM_XXX 值)。

在那里放置一个断点并开始单步执行。在您释放鼠标按钮和屏幕更新之间的某个时间,受害者将访问您正在寻找的数据结构。

要有耐心,尝试确定在任何给定时间正在做什么,但不要太深入地研究您怀疑对当前目标不感兴趣的代码。 可能需要在调试器中运行几次才能确定它。

了解正常的 win32 应用程序工作流程也有帮助。

A good point to start tracing in debugger would be on mouse up. So find main window procedure (I think tools like spyxx can inspect windows properties and event handler address is one of them). Break in to it and find where it handles mouse events -- there will be a switch, if you can recognize it in assembler (look at value of WM_XXX for mouse up in windows.h).

Put a breakpoint there and start stepping in. Somewhere between the time you released mouse button and screen being updated the victum will access the datastructure you are looking for.

Be patient, try to identify what is being done at any given time, but don't bother looking too deep into code you suspect of being uninteresting for your current objective. It might take several runs in debugger to nail it down.

Knowledge of normal win32 applications workflow helps too.

心的憧憬 2024-07-29 20:35:47

虽然严格来说它不是一个“逆向工程师的工具”,更像是一个即使像我这样的白痴也可以使用的玩具,但请查看 Cheat Engine 。 它使得跟踪内存的哪些部分、何时发生更改变得有些容易,甚至可以通过指针跟踪更改的内存部分(尽管您可能不需要)。 其中包括一个很好的交互式教程。

While not strictly a "reverse engineer's tool", and more of a toy even an idiot like me could use, check out Cheat Engine. It makes it somewhat easy to track what parts of memory have changed, when, and even has provisions for tracking the changed memory parts through pointers (though you probably don't need that). A nice interactive tutorial is included.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文