上次我们准备好了调试的环境,这次我们来看看怎么上手。
我们先给这个系列定个小目标:解析出开场 CG。
可能有朋友还记得之前我们寻找程序读取文件的部分是用 BPINT 21 3f
来下断点,
这个现在已经不能用了,这也是为什么我们不再用 dosbox 的原因。Windows 下,
打开调用的是 kernel32.dll 的 CreateFileA
。
如果有人不太了解 windows API,那么容我分享一下我的一知半解,
我们来看下这个函数:
HANDLE CreateFileA(
[in] LPCSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile
);
这个是摘自 Win32 的 API,
和 Win16 的 API 应该会有所不同,但是万变不离其宗,在 CreateFileA
中,
我们需要告诉内核我们要读取哪个文件(lpFileName
),
内核会给我们一个代表文件的 handle。
一个题外话,CreateFileA
还有一个兄弟 CreateFileW
,用来打开多字节的路径,
使用 CreateFileW
就可以识别英文字符外的路径文件,
不过我们这次没有涉及到 CreateFileW
,所以先按下不表。
经过对 Turbo Debugger for Win32(tdw) 一段简单的研究, 我没有发现如何把程序中断在打开文件的方法, 所以我们需要用 Cutter 来定位一下文件是哪里打开的。
我们打开 maplwin.exe:
加载选项默认就好:
在「导入表」的页签下,我们就可以找到 CreateFileA
的位置了:
我们也可以看到,程序并没有调用 CreateFileW
,所以如果安装路径中有中文,
那么这个程序可能会有问题,我们右击 CreateFileA
,选择「显示 X-Refs」,
来查看函数的引用(调用)位置:
只有一个地方,很好,这样我们找起来会更简单,双击左侧蓝色行:
这里应该就是调用 CreateFileA
的位置了,我们记下地址 0x0040ef64
:
根据我的一点点汇编的知识,call ebp
是呼叫 CreateFileA
,
之前的 push
则是把函数的参数压入栈中。所以,读取的文件名一定在这几个 push
中。
确认了打开文件的位置后,我们可以用 TDW 下断点,确认一下,哪个参数代表了文件名
我们现根据上一期的流程,加载 maplwin.exe,中断在程序入口:
TDW 的主界面分为四块,左上是程序,右上是寄存器,左下是内存,右下我们用不到。 蓝色的滚动条代表它是当前活动窗口,我们可以用 tab 键在四个窗口切换。
我们可以在程序窗口按 ctrl-g,来定位到打开文件的位置 0x0040ef64
,注意,
在 TDW 中,这个地址要写做 0040ef64h
,不可以写成 0x
开头的格式:
在 0040ef6bh
处按 F2 下断点,断点行会变成红色,可以稍微移动下蓝色当前行,
确认我们断点已经下了:
这时按 F9,让程序执行,程序很快就会进到断点:
我们把活动窗口调整到内存窗口,同样用 Ctrl-g,
检查 414524h
(ecx 的值)处的内存存了什么:
mpx\maplsys.cfg,这就是程序读取的第一个文件。
F9 继续程序,很快又会进入断点,如法炮制,我们就知道程序启动时打开了如下几个文件:
c7fe1ch
)有重复的内容,另外有一个位置的位置,不过这些不重要,我们注意到最后一个文件, chtitle.hhp,这个很有可能就是我们要找的资源了。
上文 Windows API 小课堂我们说到,内核打开文件成功后会返回一个 handle, 这个 handle 就保存在 eax 中,我们按 F7 执行到下一步,记录下 eax 的值:
18h
,后面我们会用它来判断是哪个文件。
我们用 cutter 和 tdw,结合一些 windows API 的知识, 确定了程序启动过程中打开了哪些文件,接下来, 我们会具体看一看程序是怎么读取这些文件的。