More selection!
如果没有看过上一篇 tips,建议先看看上一篇,了解一下 “选择” 是什么!
固件/原始二进制分析
在反汇编原始二进制文件时,IDA并不总是能够检测到代码片段,可能需要你手动尝试和试错来在整个加载范围中找到代码,这可能是一个耗时的过程。
在这种情况下,以下简单方法可能对初步寻找代码片段有用:
-
前往起始地址(IDA 反汇编视图的最顶端)(Ctrl+PgUp) -
开始选中(锚点选择,见上一篇 tips)(Alt + L) -
前往结束地址(IDA 反汇编视图的最底端)(Ctrl+PgDn)你也可以前往任何你想结束的位置,比如在一堆 0xFF 的前面。 -
点击菜单项【Edit】→ 【Code】 或快捷键 C,你将看见一个对话框,询问你更加具体的操作,如下图
-
点击 “Force”,如果选择的范围里大部分都是指令。或者 “Analyse”,如果选择的范围里指令之间有数据。 -
IDA 会分析你选择的区间,并尝试将未定义的数据转换成指令。如果选定区域确实有有效的代码,你可能会看到函数被添加到函数窗口中(可能包括一些误报)。
结构体偏移
另一个“选择”的有用应用是对多个指令应用结构偏移。例如,让我们考虑一个来自UEFI模块的函数:
.text:0000000000001A64 sub_1A64 proc near ; CODE XREF: sub_15A4+EB↑p
.text:0000000000001A64 ; sub_15A4+10E↑p
.text:0000000000001A64
.text:0000000000001A64 var_28 = qword ptr -28h
.text:0000000000001A64 var_18 = qword ptr -18h
.text:0000000000001A64 arg_20 = qword ptr 28h
.text:0000000000001A64
.text:0000000000001A64 push rbx
.text:0000000000001A66 sub rsp, 40h
.text:0000000000001A6A lea rax, [rsp+48h+var_18]
.text:0000000000001A6F xor r9d, r9d
.text:0000000000001A72 mov rbx, rcx
.text:0000000000001A75 mov [rsp+48h+var_28], rax
.text:0000000000001A7A mov rax, cs:gBS
.text:0000000000001A81 lea edx, [r9+8]
.text:0000000000001A85 mov ecx, 200h
.text:0000000000001A8A call qword ptr [rax+50h]
.text:0000000000001A8D mov rax, cs:gBS
.text:0000000000001A94 mov r8, [rsp+48h+arg_20]
.text:0000000000001A99 mov rdx, [rsp+48h+var_18]
.text:0000000000001A9E mov rcx, rbx
.text:0000000000001AA1 call qword ptr [rax+0A8h]
.text:0000000000001AA7 mov rax, cs:gBS
.text:0000000000001AAE mov rcx, [rsp+48h+var_18]
.text:0000000000001AB3 call qword ptr [rax+68h]
.text:0000000000001AB6 mov rax, [rsp+48h+var_18]
.text:0000000000001ABB add rsp, 40h
.text:0000000000001ABF pop rbx
.text:0000000000001AC0 retn
.text:0000000000001AC0 sub_1A64 endp
如果我们知道 gBS
是指向 EFI_BOOT_SERVICES
的指针,我们就可以将对它的访问(在调用指令中)转换为结构偏移。()
我们可以手动为每个访问执行此操作,但这很繁琐。
在这种情况下,选择功能就非常有用。如果我们选择访问结构的指令并按下 T 键(结构偏移),将会弹出一个新对话框:
你可以选择将哪个寄存器用作基寄存器,应用哪种结构,甚至可以选择要转换的具体指令。
选择 rax
和 EFI_BOOT_SERVICES
后,我们得到了一个看起来很整洁的列表:
.text:0000000000001A64 sub_1A64 proc near ; CODE XREF: sub_15A4+EB↑p
.text:0000000000001A64 ; sub_15A4+10E↑p
.text:0000000000001A64
.text:0000000000001A64 Event = qword ptr -28h
.text:0000000000001A64 var_18 = qword ptr -18h
.text:0000000000001A64 Registration = qword ptr 28h
.text:0000000000001A64
.text:0000000000001A64 push rbx
.text:0000000000001A66 sub rsp, 40h
.text:0000000000001A6A lea rax, [rsp+48h+var_18]
.text:0000000000001A6F xor r9d, r9d ; NotifyContext
.text:0000000000001A72 mov rbx, rcx
.text:0000000000001A75 mov [rsp+48h+Event], rax ; Event
.text:0000000000001A7A mov rax, cs:gBS
.text:0000000000001A81 lea edx, [r9+8] ; NotifyTpl
.text:0000000000001A85 mov ecx, 200h ; Type
.text:0000000000001A8A call [rax+EFI_BOOT_SERVICES.CreateEvent]
.text:0000000000001A8D mov rax, cs:gBS
.text:0000000000001A94 mov r8, [rsp+48h+Registration] ; Registration
.text:0000000000001A99 mov rdx, [rsp+48h+var_18] ; Event
.text:0000000000001A9E mov rcx, rbx ; Protocol
.text:0000000000001AA1 call [rax+EFI_BOOT_SERVICES.RegisterProtocolNotify]
.text:0000000000001AA7 mov rax, cs:gBS
.text:0000000000001AAE mov rcx, [rsp+48h+var_18] ; Event
.text:0000000000001AB3 call [rax+EFI_BOOT_SERVICES.SignalEvent]
.text:0000000000001AB6 mov rax, [rsp+48h+var_18]
.text:0000000000001ABB add rsp, 40h
.text:0000000000001ABF pop rbx
.text:0000000000001AC0 retn
.text:0000000000001AC0 sub_1A64 endp
强制字符串常量
当一些代码引用字符串时,IDA通常足够智能地检测到它,并将引用的字节转换为文字项。然而,在某些情况下,自动转换不起作用,例如:
-
字符串包含非ASCII字符; -
字符串没有以空字符结尾。
一个常见的例子是 Linux 内核,它使用特殊的字节序列来标记内核消息的不同类别。
例如,考虑 joydev.ko 模块中的这个函数:
IDA没有在1BC8处自动创建字符串,因为它以一个非ASCII字符开头。然而,如果我们选择字符串的字节并按下快捷键(转换为字符串),字符串仍然会被创建:
从数据创建结构体
当处理二进制文件中的结构化数据时,这个动作非常有用。我们考虑一个表,其条目大致有如下布局:
struct copyentry {
void *source;
void *dest;
int size;
void* copyfunc;
};
虽然这样的结构体总是可以在结构体窗口中手动创建,但通常先格式化数据再创建描述它的结构体更容易。
创建四个数据项后,选择它们,从上下文菜单中选择 “Create struct from selection”:
IDA 将创建一个代表所选数据项的结构体,然后可以使用它来格式化程序中的其他条目,或在反汇编中更好地理解与这些数据交互的代码。
原文始发于微信公众号(二进制磨剑):IDA 技巧(4) More selection!
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论