IDA 中的细分市场。如何克服 NONAME 问题

逆向工程 艾达 拆卸 分割
2021-06-12 09:47:37

我正在拆解打包的 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:1SEG:偏移地址对应于线性地址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

注意外部参照。所以IDA实际上正确地处理了调用!为什么调用被认为是无效的?IDA 帮助文件是这样说的:

问题:找不到名字

描述

有两个原因可能导致此问题:

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

该怎么办

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

    • 尝试手动输入操作数
    • 或者通过创建新段使非法地址合法
  2. 否则,数据库已损坏。

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

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

IDA 中的段有什么意义?如果DATA段可以包含指令,CODE段可以作为数据读写吗?

请原谅我提出这样一个新手问题,但众所周知,官方 IDA 手册非常稀缺,而且 HexRays 论坛对我来说是关闭的,因为我使用的是免费软件版本。

3个回答
  1. 您的程序正在使用带有 base1AD9h的段(远调用的段部分)。您需要创建一个与之匹配的新段。

    Start = 0x1AD90   (0x1AD9<<4)
    End = 0x2AD90  [for example] (start + 64KB - maximum size)
    Base = 0x1AD9
    (o) 16-bit
    
  2. 现在,通过新的部分并确保一切都有意义。如有必要,修剪该段(减少结束地址)。

  3. 使用不同的段值查找另一个远跳/调用。对新基地重复步骤 1。

  4. 做数据段相同的(寻找装入值ds/ es/ ss)。

我曾经处理过一个 ROM 映像并遇到了这个问题。在伊戈尔提出他的建议之前,我也很困惑该怎么做。

似乎发生的事情是链接器将每个目标文件放入其自己的段中,因此每个对象间函数调用都在二进制文件中呈现为远调用,其中段基是提供给模块内所有函数的基. 即,您在回复 Igor 评论时提到的案例对我来说没有实现。

为了修复它,我在二进制文件中搜索了所有远调用指令,然后在每个引用的 x86 段的线性地址处创建了一个新的 IDA 段(尽可能大)。即,我确实最终得到了很多微小的片段。这不是一个真正的问题;实际上,问题在于如果不这样做,引用将无法正确反汇编。这是非常快速的工作,可能可以通过脚本实现自动化。

这背后的问题是每个段的地址最大为 64KB,为了生成有意义的汇编,IDA 需要知道在执行代码时段寄存器应该是什么。

假设您在线性地址处有以下代码0x23456

mov bx, 6789
call [bx]

这调用了哪个函数?好吧,如果您的 CS 寄存器有0x2000,而您的 IP 是0x3456,那么这将调用2000:6789or (linear) 0x26789但是,同样,您可以0x2345在 CS 和0x0006IP 中进行。在这种情况下2345:6789or ( 0x23450+ 0x6789=)0x29BD9被调用。

在某些情况下,跳转/调用目标不是那么模糊,例如绝对远调用,如您的call 1AD9:1,或相对跳转(这就是我使用间接[bx]调用的原因;call 6789将使用与 IP 相关的汇编指令,所以独立于段)。

尽管如此,如果您不知道它们属于哪个段,则偏移没有意义。如果你有这样的代码

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

你想要一个变量定义(对于 bx) at 1234:abcd,另一个(对于 dx) at 5678:cdef这意味着 IDA 必须知道一个段从 at 开始1234放入第一个变量,另一个段从 at 开始放入5678第二个变量。(我使用推/弹出,因为据我所知,没有处理器操作码直接加载段寄存器,我认为移动它们也有一些限制,所以推/弹出被大量用于加载他们)。

当然,段的要点是它们是一个坏主意并造成了很多麻烦,但英特尔希望能够用 16 位处理器处理超过 64K 的地址,因此他们发明了它们。这意味着它们存在,我们需要在反汇编 16 位程序时正确使用它们。我们是否喜欢他们不是问题。

您能做的最好的事情就是找到尽可能多的段引用 - 初始 CS/DS/ES/SS 值、对某个cs:ip位置的远调用以及加载到段寄存器中的值。然后,记下出现哪些段值,假设它们中的每一个都足够大以容纳下一个的所有空间,并将此列表提供给 IDA。