我正在分解一个压缩的16位DOS MZ EXE。

为了对它进行模糊处理,我在解压缩例程的结尾处的DOSbox中设置了一个断点,让其运行并进行了内存转储。这样,我基本上得到了经过模糊处理的EXE映像。

将映像加载到IDA中时开始出现问题。您知道,我不理解IDA的细分概念。它们类似于x86段,但是有许多我无法理解的差异。当IDA要求我创建至少一个段时,我只是制作了一个1 MB长的巨大段,因为程序地址空间中的代码和数据是混合的,因此引入诸如CODEDATA等单独的段是没有意义的。

显示IDA入口点后,一切工作正常:IDA成功确定了函数,局部变量,参数等。唯一的问题是,即使某些调用指向正确的子例程,它们也被标记为NONAME 。最奇怪的是,这些子例程对“非法”调用具有正确的XREF。这是一个示例:

seg000:188FF 004                 call    1AD9h:1         ; Call Procedure


这行是红色的,并且在问题列表中有相关​​的NONAME问题。为什么?

1AD9h:1 seg:offset地址对应于线性地址0x1ad91,其线性地址为:

seg000:1AD91     ; =============== S U B R O U T I N E =======================================
seg000:1AD91
seg000:1AD91     ; Attributes: bp-based frame
seg000:1AD91
seg000:1AD91     sub_1AD91       proc far                ; CODE XREF: sub_188F2+DP


注意XREF。因此IDA实际上可以正确处理该呼叫!为什么通话被视为无效? IDA帮助文件说:


问题:找不到名称
描述

导致此问题的两个原因:


在反汇编的程序中引用了非法地址;
IDA找不到该地址的名称,但它必须存在。

该怎么办



如果此问题是由引用非法地址引起的


>尝试手动输入操作数
,或者通过创建新的段来使非法地址合法。


否则,数据库将损坏。


因此,我想问题是我有一个庞大的段,而不是几个小的段。但是,如何正确地将地址空间划分为适当的段?

我知道入口点的寄存器值(包括DSCSSSIP等)。假设我从入口点对应于CS寄存器值的段开始创建一个CODE段。但是该网段应该有多长?

IDA中的网段到底有什么意义?如果DATA段可以包含指令,并且CODE段可以作为数据读取和写入?我使用免费版本。

#1 楼



您的程序正在使用以1AD9h为基的段(far调用的段部分)。您需要创建一个与其匹配的新细分。

Start = 0x1AD90   (0x1AD9<<4)
End = 0x2AD90  [for example] (start + 64KB - maximum size)
Base = 0x1AD9
(o) 16-bit


现在,遍历新的细分市场并确保一切都有意义。如有必要,修剪段(减少结束地址)。
使用另一个段值查找另一个远跳转/调用。对新的基数重复步骤1。
对数据段执行相同操作(查找装入到ds / es / ss中的值)。


评论


因此,您是在暗示对于每个远距离呼叫,IDA应该有一个基数等于该呼叫的段部分的段?但是x86段可以重叠,而IDA段不能重叠。如果我创建了一个从0x1AD90开始的段后,却偶然发现对段1ADAh的远距离调用怎么办?我是否应该将第一个段缩小到16个字节,以便能够创建一个从0x1ADA0开始的新段?看起来我最终会遇到很多小片段。我怀疑这是预期的方法。

– ScumCoder
14年8月13日在18:42

实际程序很少使用重叠的段。

–伊戈尔·斯科钦斯基♦
2014年8月13日18:50

#2 楼

我曾经处理过ROM映像,然后遇到了这个问题。在Igor提供他的建议之前,我也很困惑。

似乎正在发生的事情是,链接器将每个对象文件都放在了自己的段中,因此呈现了每个对象间函数在二进制文件中作为far调用,其中段基础是模块内所有功能的基础。也就是说,您在答复Igor的评论时提到的案例对我而言并不重要。可能)在每个引用的x86段的线性地址处。也就是说,我确实确实有很多细分。这不是真正的问题。确实,问题在于如果不这样做,引用将无法正确反汇编。这是非常快速的工作,并且可能可以使用脚本来自动化。

评论


那就对了。但是可以有一个程序用两个不同的段值调用一个相同的函数。例如,函数从地址0x12345开始,从一个地方被调用为远1233h:0015h,从另一个地方被调用为远1234h:0005h。它可能“很少在实际程序中使用”,但是在物理上是可能的,在这种情况下,将无法以两个调用都不会出现NONAME问题的方式来设置func的段。奇怪的是,像IDA这样强大的工具在任何情况下都可能会遇到问题,无论它的理论性如何。

– ScumCoder
2014年8月13日在20:31

是的,它肯定会发生:为了混淆,或者只是一些汇编编码器很聪明。关于IDA及其局限性,它是我一直以来最喜欢的软件,与SoftICE一起使用,但是在其长期开发过程中已经做出了许多架构决策,这些决策可能会使您的反向努力绊倒。在我从事软件开发工作之前,这种事情曾经使我感到沮丧和烦恼,现在我明白了。考虑到有限的资源,Hex-Rays可以制造出惊人的产品。您几乎可以完成所有您想做的事情,支持非常好,SDK / IDAPython很好。做柠檬水。

–滚轴
14年8月13日在20:53

#3 楼

这背后的问题是每个段最多可处理64KB,并且要生成有意义的汇编,IDA需要知道执行代码时段寄存器应该是什么。

线性地址0x23456的以下代码:

mov bx, 6789
call [bx]


此函数调用哪个函数?好吧,如果您的CS寄存器具有0x2000,并且您的IP是0x3456,那么它将调用2000:6789或(线性)0x26789。但是,同样,您可以在CS中使用0x2345,在IP中使用0x0006。在这种情况下,会调用2345:6789或(0x23450 + 0x6789 =)。相对跳转(这就是为什么我使用间接0x29BD9调用的原因; call 1AD9:1将使用相对于IP的汇编指令,因此与段无关)。

不过,如果您不知道偏移量属于哪个段,则偏移量就没有意义。如果您具有这样的代码

mov ax, 1234
push ax
pop es
mov bx, es:[abcd]
mov ax, 5678
push ax
pop es
mov dx, es:[cdef]


,则需要在[bx]处定义一个变量定义(用于bx),在call 6789处定义另一个变量(用于dx)。这意味着IDA必须知道一个段从1234:abcd开始以放入第一个变量,而另一段从5678:cdef进入第二个变量。 (我使用push / pop是因为据我所记得,没有处理器操作码直接加载段寄存器,而且我认为移动它们也有一些限制,因此push / pop被大量使用

当然,分段的要点是它们是一个坏主意,并且造成了很多麻烦,但是Intel希望能够使用16位处理器解决64K以上的问题,发明了它们。这意味着它们存在,并且在拆卸16位程序时我们需要使它们正确。我们是否喜欢它们不是问题。

最好的办法就是找到尽可能多的段引用-初始CS / DS / ES / SS值,对某些1234位置的远调用以及装入段寄存器的值。然后,记下发生哪些段值,假设每个段值都足够大以容纳下一个段的所有空间,然后将此列表提供给IDA。

评论


谢谢,但是我知道x86分割系统。似乎与IDA细分无关。 (1)我正在分解的代码几乎没有近距离跳转,只有短距离和远距离的不需要适当的段。 (2)我提到的调用1AD9h:1指令是一个远距离跳转(操作码9a 01 00 d9 1a),它仍然标记为无效。 (3)我仍然不明白为什么IDA设法针对此“无效”调用生成了适当的XREF。 (4)AFAIK不是通过“程序分段”视图而是通过“分段寄存器”视图来控制分段寄存器的值,这似乎是另一回事。

– ScumCoder
2014年8月13日在17:42