可变大小结构是一种用于处理可变大小二进制结构的构造,具有编译时类型检查的优势。
在源代码中
通常,这种结构使用类似以下的布局:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
structvarsize_t
{
// 开头的一些固定字段
int id;
size_t datalen;
//[更多字段]
unsigned char data[]; // 可变部分
};
换句话说,开头是固定布局部分,末尾是未指定大小的数组。
一些编译器不喜欢[]
语法,因此也可以使用[0]
或甚至[1]
。在运行时,结构的空间是使用完整大小分配的,数组可以像具有预期大小一样访问。例如:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
structvarsize_t* allocvar(int id, void *data, size_t datalen);
{
size_t fullsize = sizeof(varsize_t) + datalen + 1;
structvarsize_t *var = (structvarsize_t*) malloc(fullsize);
var->id = id;
var->datalen = datalen;
memcpy(var->data, data, datalen);
var->data[datalen] = 0;
return var;
}
IDA能处理这样的结构吗?可以,但你需要注意一些特殊情况。
在反编译器中
在反编译器中一切都很简单:只需使用C语法将结构添加到本地类型中,并将其用于局部变量和函数参数的类型。反编译器会自动检测对可变部分的访问并相应地表示它们。
在反汇编中
然而,反汇编视图更棘手。你可以从本地类型导入结构到IDB结构,或者通过在末尾显式添加0元素的数组手动创建一个:
ounter(lineounter(lineounter(lineounter(lineounter(line
00000000 varsize_t struc ; (sizeof=0x8, align=0x4, copyof_1, variable size)
00000000 iddd ?
00000004 datalen dd ?
00000008 data db 0 dup(?)
00000008 varsize_t ends
但当你在数据区有这样的结构实例时,使用这个定义只覆盖固定部分。要扩展结构,使用*
(创建/调整数组大小操作)并指定结构的完整大小。
示例
最近的微软编译器向PE可执行文件中添加了所谓的“COFF组”信息。IDA目前尚未完全解析,但在反汇编列表中用注释IMAGE_DEBUG_TYPE_POGO
标记:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
.rdata:004199E4 ; 调试信息 (IMAGE_DEBUG_TYPE_POGO)
.rdata:004199E4 dword_4199E4 dd 0 ; 数据引用: .rdata:004196BC↑o
.rdata:004199E8 dd 1000h, 25Fh, 7865742Eh, 74h, 1260h, 0BCh, 7865742Eh, 69642474h, 0
.rdata:00419A0C dd 1320h, 11BE2h, 7865742Eh, 6E6D2474h, 0
.rdata:00419A20 dd 12F10h, 12Ch, 7865742Eh, 782474h, 13040h, 164h, 7865742Eh, 64792474h
.rdata:00419A20 dd 0
.rdata:00419A44 dd 14000h, 11Ch, 6164692Eh, 35246174h, 0
.rdata:00419A58 dd 1411Ch, 4, 6330302Eh, 6766h, 14120h, 4, 5452432Eh, 41435824h, 0
.rdata:00419A7C dd 14124h, 4, 5452432Eh, 41435824h, 41h, 14128h, 1Ch, 5452432Eh, 55435824h
在展开数组或查看十六进制视图时,很明显它存储了可执行文件的原始节名称信息,在链接器合并它们之前。因此,格式化此信息可能很有用。它似乎由以下结构列表组成:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
struct section_info
{
int start; // RVA
int size;
char name[]; // 以零结尾
};
如果需要,将字符串用零填充以对齐每个结构在4字节边界上。
创建本地类型并将结构导入IDB后,我们可以取消定义IDA创建的数组,并开始在区域中使用编辑 > 结构变量…(Alt–Q)创建结构实例。然而,默认情况下只覆盖固定部分:
要扩展结构,按*
并输入完整大小。例如,第一个应该是14(8用于固定部分,6用于“.text”和终止零),尽管你也可以使用建议的16:
现在结构具有正确的大小并覆盖字符串,但它以十六进制字节而不是文本形式打印。为什么以及如何修复?
当IDA将C类型转换为汇编级别(IDB)结构时,它仅依赖于C类型的大小,因为在汇编级别上字节和字符之间没有区别。因此,字符数组与字节数组相同。然而,你仍然可以应用额外的表示标志来影响结构的格式化。例如,你可以转到结构列表中的导入定义,并将name
字段标记为字符串字面量,无论是从上下文菜单还是按A
:
字段现在相应地被注释,数据实例显示为文本:
事实上,一旦你将字段标记为字符串,新声明的实例将由IDA使用零终止符自动调整大小。
学习资源
立即关注【二进制磨剑】公众号
原文始发于微信公众号(二进制磨剑):IDA技巧(94)可变大小结构
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论