DOS 程序段前缀和加载的可执行文件的基地址之间有任何关联吗?

逆向工程 拆卸 x86 dos-exe 分割
2021-06-29 20:50:48

我正在使用 IDA 来反汇编Test Drive III这是 1990 年的 DOS 游戏。*.EXE 具有 MZ 格式。

游戏采用了一些防反转功能,如复制其代码段(PSPseg+2be7),其中PSPseg是的初始值ES(即,段,其中PSP驻留)。据我所知,COM 可执行文件总是在结束后立即加载Program Segment Prefix (PSP),因此 PSP 和 exe 都适合一个段。MZ 可执行文件呢?应用程序的 PSP 相对于应用程序本身是否有固定的位置?

换句话说,base-PSPseg偏移量总是相同的吗?在我的DOSBox上,程序执行开始时CS总是0x22CF, ES=DS=0x01FE,CS0在 MZ 标头中是0x20C1,产生base-PSPseg偏移量0x0010(16 段,256 字节 - 正好是 PSP 的大小)。

如果这个偏移量不是固定的,并且 PSP 和应用程序只是随机加载到任何足够大的内存位置,那么至少对它们的地址有任何保证?就像 PSP 地址总是低于应用程序的基址一样?

4个回答

DOS 没有能够同时运行多个应用程序的概念,每个应用程序都可以分配内存。终止后保持驻留的程序无法在另一个程序运行时分配更多内存。因此,内存中没有碎片,也没有“足够大的内存位置”。

从 0x00000 开始的内存被中断向量表和各种 bios 变量占用,DOS 加载在它们后面。DOS 使用的内存块的末尾取决于很多因素,但是每当加载程序时,PSP 都会在尽可能低的位置创建,然后程序直接加载在其后面。所以,是的,您可以依靠使用 PSP 后面的内存的程序,并且您也可以依靠段寄存器和 PSP 之间的差异保持不变。

PSP 段本身对程序员来说是不可预测的——像 ansi.sys 控制台驱动程序、外来键盘驱动程序、Cdrom 驱动程序和各种常驻程序(有人记得吗?)可能会增加“dos 块”的大小,导致加载程序的 PSP 地址增加(可用内存减少)。但是只要您不更改 DOSBOX 的配置,您就应该能够依赖 PSP - 以及您的段寄存器 - 对于程序的每次运行都是相同的。

MZ 格式的可执行文件从文件加载数据之前的CS-0x10 处也有 PSP

引用技术帮助!,可能是最好的DOS编程参考:

EXE 格式的程序定义了多个程序段,包括代码段、数据段和堆栈段。EXE 文件从 PSP:0100 开始加载。

通常情况下,DOS 会.EXE在 PSP 之后立即加载程序,用您的话来说,这意味着基于 - PSP == 0x0010. 但这不一定是真的,如果您正在编写这个(或任何)DOS 可执行文件,您最好了解这一点。然而,这不是你。相反,在这里,您是一名逆向工程师 - 或者至少尝试成为一名逆向工程师。因此,重要的不是 DOS 在所有一般情况下的真实情况,而是程序员对您正在研究的特定可执行文件的想法,无论是对还是错。

如果这个程序将它的一些代码重定位到它通过添加我认为你所说的硬编码0x2BE7计算的段PSPseg,那么程序员很可能依赖于他的程序的基地址是PSPseg + 0x0010当然,更精确地了解来自哪里0x2BE7可能表明程序员已经更小心了。并不是说我在很长很长的时间里对任何 DOS 代码进行了逆向工程,但我记得映射程序的分段(包括它对未初始化数据、堆和堆栈的允许)通常是一种有用的准备。

现在来概括一下……无论“可能是最好的 DOS 编程参考”中怎么说,DOS 程序员不应该依赖于在 PSP 之后立即加载的程序材料。尽管在实践中绝大多数情况下都是正确的,但存在一个例外情况并且是:首先,记录在案,如果模糊不清;其次,不完全在程序员的控制范围内。

当 的e_maxalloc成员IMAGE_DOS_HEADER为零时,就会出现这种情况这可以在链接时设置,也可以在以后通过诸如EXEHDR. DOS 将这个成员的零解释为在 DOS 找到的用于加载程序的任何内存块中以不寻常的方式排列程序的方向。像往常一样,PSP 位于底部,但程序素材在块内放置得尽可能高。之间的空间未初始化。

PSP 是一个进程表,它保存有关正在运行的进程(应用程序)的信息,它由加载程序创建和填充。加载器使用 EXEHDR 信息来填充 PSP 的一些字段,否则。创建 PSP 后,加载程序获取应用程序代码(EXEHDR 之后到文件末尾的任何内容,并在 PSP 之后加载。

PSP 占用应用程序开头(COM 和 EXE)分配的内存;段中的 PSP 偏移量始终为 0000H - 100H。当您的应用程序启动时,DS 和 ES 段指向 PSP,您必须在继续之前将它们调整为您的应用程序数据段。

段不是固定的,它们是根据可用内存空间定义的,但是段内的偏移量始终相同,否则您的应用程序会崩溃,您的所有数据和代码都是根据从段开始处的偏移量定义(计算)的.

CS在分配内存并加载你的程序后由加载器填充,IP也由加载器填充,这个信息是从EXEHDR中提取的(应用程序入口点的偏移量(由汇编器/编译器在构建时计算)目标代码),但它是由程序员在汇编中定义的,它是masm中指令END后设置的标签,或由编译器设置,在c中是主函数偏移量)。