所以,一个完整的答案值得写一系列博客文章,但我会尝试触及高点:
虽然您可以使用不同的 seg:base 对来引用相同的位置,但在实际代码中这种情况很少发生。代码段对它们的所有功能使用相同的基数,并且不与邻居相交。但是,它们有时不会在恰好 16 字节对齐的边界处开始或结束。此 ROM 中的一个示例:
- 具有基数的段
E0470从E047:0008(线性 0xE0478)开始,到(线性 0xF03F4)结束E047:FF84。
- 以下段使用基数
F03F,因此从F03F:0004(也是线性的 0xF03F4)开始
在代码段内,近跳转和调用通常留在段内,并且函数不应超出段的末尾。这是确定段的真实边界的方法之一。例如,对于二进制文件中的基本 E047,我们有:
- 调用到
E047:0008,所以这可能是该段的开始。
- far 调用
E047:FF53可能是该段的最后一个函数。通过反汇编函数并仅跟随附近的跳转,我们来到了retf0xF03F2,或者E047:FF82,看起来该段在那里结束,我们可以在它之后添加额外的零字节,它可能是由链接器添加的用于填充,到达在E047:FF84(线性 0xF03F4)的最终边界处。
更扩展的例子:在初步猜测段的C84B边界为C84B:0004to 后C84B:F112,我注意到问题列表中的这些说明:
SEG_C84B:96A2 call 0F2D5h
SEG_C84B:EF69 jmp 0F395h
SEG_C84B:F0C8 call 0F3DFh
由于这些是接近呼叫和跳跃,它们应该属于同一段。跟随它们为我们提供了段的扩展端为 0xd7894 或 C84B:F3E4。
- 作为上述的扩展,使用的数据引用
cs应该指向段内的数据,并且在上下文中应该是有意义的。这在类似于以下内容的 switch 语句中最为明显:
2E FF A7 B6 59 jmp cs:off_B000_59B6[bx]
2E FF A4 8E 76 jmp cs:off_C000_768E[si]
一个很好的例子是 0xf05f1 处的这个:
2E FF A7 06 02 jmp cs:206h[bx]
68 01 dw 168h
6E 01 dw 16Eh
74 01 dw 174h
80 01 dw 180h
86 01 dw 186h
如果我们假设跳转后的单词是跳转表,那么段应该0206h更早一些字节开始,并且确实有一个函数从 0xf03f4 开始,所以如果我们从那里开始一个段(以 0xf03f 为基数),我们的开关是很好地恢复:
SEG_F03F:01FD D1 E0 shl ax, 1
SEG_F03F:01FF 8B D8 mov bx, ax
SEG_F03F:0201 2E FF A7 06 02 jmp cs:off_F03F_206[bx]
SEG_F03F:0206 68 01 dw offset loc_F03F_168 ; DATA XREF: sub_F03F_100+101↑r
SEG_F03F:0208 6E 01 dw offset loc_F03F_16E
SEG_F03F:020A 74 01 dw offset loc_F03F_174
SEG_F03F:020C 80 01 dw offset loc_F03F_180
SEG_F03F:020E 86 01 dw offset loc_F03F_186
SEG_F03F:0210 95 01 dw offset loc_F03F_195
我还没有完整的图片,但以下界限似乎很接近:
B000: 0xB0000-0xC0000 (full 64K)
C000: 0xC0000-0xC84B4
C84B: 0xC84B4-0xD7A52 (there is some weird stuff near the end)
E047: 0xE0478-0xF03F4
F03F: 0xF03F4-0xFDAF0
FDAF: 0xFDAF0-0xFFFF0
我在gist 中添加了一些我曾经使用过的脚本。
注意:扫描仪打印出来的一些地址是误报,并不代表真正的段。您应该只考虑具有多个匹配项的那些。
较低段中的数据似乎被压缩了。