IDA分割问题

逆向工程 艾达 分割
2021-07-08 05:14:56

在我尝试逆向工程的程序中,最初 IDA 只创建了三个段:CODE,DATA.idata.

使用来自编译器源代码的信息(http://pastebin.com/ParJ3683第 30-78 行),我确定事实上 DATA 是由(至少)四个段DATAINITDATAEXITDATA和 连接起来的BSS

所以我创建了三个具有指定对齐方式(word/dword)和适当边界的新段;虽然所有数据都出现在正确的段边界中,但所有对数据段中和现在在 BSS 中的内容的引用现在完全混乱:

CODE:00401A8A                 push    ds:globalErrorMessagesCaption[eax*4] ; lpCaption

转换成

CODE:00401A8A                 push    dword ptr ds:(algn_481049+187h)[eax*4] ; lpCaption

部分标题是

_BSS:00481106 ; Segment type: Pure data
_BSS:00481106 _BSS            segment dword public 'DATA' use32
_BSS:00481106                 assume cs:_BSS
_BSS:00481106                 ;org 481106h

这里出了什么问题,我该如何解决?

2个回答

简短回答:您新创建的段可能有一个无效的选择器值 0,一旦您将选择器设置为有效值(例如SetSegmentAttr(here, SEGATTR_SEL, 1)在命令提示符下),事情就会重新开始工作原因是 IDA 为 PE32(+) 创建的所有选择器都具有相同的基数并且大部分是等效的。

长答案:

最近的 IDA 版本在通过 UI 进行片段编辑的方式上存在某些差异。通过 UI 进行片段编辑可能会导致复杂的操作,并且由此产生的行为并不总是直观的。所以我宁愿不给出UI操作方面的解决方案。

IDC 和 Python 中可用的段编辑功能要简单得多,而且可以从 IDA 的命令提示符调用它们(包括标志值的符号名称,如 SEGMOD_KEEP)。对于更棘手的事情,我发现在代码片段编辑器 (Shift-F2) 中将我自己的操作组合为命名片段是最方便的。在操作结束时使用合适的 Jump() - 将光标定位在下一个操作最有可能的候选者上 - 这使得很容易在短时间内完成很多事情。

IDC/Python 段编​​辑功能比整个 UI 内容的文档要好一些,而且更简单,它们更容易通过实验来处理。并且不太容易出现令人讨厌的惊喜。如果发生了不好的事情——就像你的情况一样——通常可以通过使用如下命令在一个 IDA 实例中探测一个工作示例来修复它们:

GetSegmentAttr(here, SEGATTR_FLAGS)

然后切换到另一个包含“患者”的 IDA 实例并输入新发现的值:

SetSegmentAttr(here, SEGATTR_FLAGS, 0x10)

由于两个 IDA 实例共享相同的命令提示符历史记录,因此输入第二个命令就像点击向上箭头并修改在另一个 IDA 实例中输入的查询一样简单。在您的情况下,如果您将新创建的段的SEGATTR_SEL设置为与现有(因此正确配置)段相同的值,事情可能会再次开始工作为了找到魔法标志和它们的名字,我更喜欢使用 grepping 而$(IDA)/idc/idc.idc不是查阅帮助文件,因为 idc.idc 往往更全面和最新。

注意:像“Program Segmentation”这样的视图窗口不会自行刷新,但可以通过再次按下热键(例如“Program Segmentation”的 Shift-F7)以及在命令提示符下输入“RefreshLists()”来刷新它们. 此外,我在这里写的所有东西可能都远非最佳,但根据我有限的知识和现有文档,这是我能做的最好的......

最后但并非最不重要的一点是,段对于具有平面地址空间的 PE32(+) 目标不是很重要。我发现它们可以方便地将代码范围标记为属于某个模块(例如“CRT”、“Lua”等),但实际上没有必要创建源代码中包含的段的忠实表示,因为它们是主要是为了链接器的利益,最终被合并到少量的 PE 部分。

除非该段具有有效的选择器,否则 IDA 无法引用该段中的符号,因此这是为新段设置的最重要的属性。选择器可以在“创建段...”对话框中通过“段落基础”字段进行设置。

GetSegmentAttr(here, SEGATTR_SEL)如果光标位于要拆分的段中,则告诉您正确的选择器是什么。您可以在对话的输入字段中直接输入表达式;出于同样的原因,您可以here在开始地址字段和SegEnd(here)结束地址字段中输入。

这是一个 IDC 片段 (Shift-F2),它在当前位置拆分当前段,克隆最相关的属性(包括 SEGATTR_SEL):

CompileEx(
  "class HelperFunctions {"
    "copy_seg_attr (src, dst, attr) { SetSegmentAttr(dst, attr, GetSegmentAttr(src, attr)); }"
  "};", 0  );

auto ori_seg, ok, fn;

ori_seg = SegStart(here);

ok = AddSegEx(
   here, 
   SegEnd(here), 
   GetSegmentAttr(here, SEGATTR_SEL),
   GetSegmentAttr(here, SEGATTR_BITNESS),
   GetSegmentAttr(here, SEGATTR_ALIGN),
   GetSegmentAttr(here, SEGATTR_COMB),
   ADDSEG_NOSREG  );

if (!ok)  return Warning("AddSegEx() failed");

fn = HelperFunctions();    
fn.copy_seg_attr(ori_seg, here, SEGATTR_PERM);
fn.copy_seg_attr(ori_seg, here, SEGATTR_FLAGS);
fn.copy_seg_attr(ori_seg, here, SEGATTR_ES);
fn.copy_seg_attr(ori_seg, here, SEGATTR_SS);
fn.copy_seg_attr(ori_seg, here, SEGATTR_DS);
fn.copy_seg_attr(ori_seg, here, SEGATTR_TYPE);
fn.copy_seg_attr(ori_seg, here, SEGATTR_COLOR);

RenameSeg(here, AskStr("", "segment name"));

注意:通过 Compile() 或 CompileEx() '内联'一个类定义是我发现的在代码片段中定义函数的唯一技巧。它似乎只适用于较新的 IDA(用 6.6 和 6.7 测试),并且仅当 Compile() 位于代码段的开头时。

对于较旧的 IDA 版本,辅助函数必须以其他方式定义,例如在单独的 .idc 文件中,并且对 AddSegEx() 的调用需要替换为 AddSeg()。

另外,我找不到查询段类的方法,因此即使有SetSegClass()可以设置它的函数也无法复制它。

逆操作——将下一段合并到当前段——要简单得多:

auto seg_beg, seg_end, next_seg;

seg_beg = SegStart(here);

if (seg_beg == -1)  return Warning("no segment here");

next_seg = NextSeg(here);

if (next_seg == SegEnd(here))
{
   seg_end = SegEnd(next_seg);

   DelSeg(next_seg, SEGMOD_KEEP);
   SetSegBounds(seg_beg, seg_beg, seg_end, SEGMOD_KEEP);

   Jump(PrevNotTail(seg_end));
}

使用这样的代码段,您只需单击每个段即可合并一整船段,这对于内存转储非常方便。或者为了撤销意外的段拆分......