C++方向逆向STL
什么是STL
STL是C++标准模板库,内置了很多常用的数据结构和算法,例如动态数组、队列等。故STL在日常编程开发中很常用,故在逆向过程中学习STL是必要的。
逆向中STL的主要问题:
- 缺少符号,无法判断STL容器类型
- STL内部数据结构相当复杂,难以提取数据
- O3优化使用大量STL函数被内联
O3优化是为了提高程序编译运行速度采用的优化方式,函数内联是将函数代码直接嵌入原代码中,减少了函数调用带来的损失。
这使得逆向过程中会很难分清是STL的代码还是用户自己的代码。
常见的STL容器:
- std::string 字符串
- std::vector 动态数组
- std::map 关联数组
案例介绍
无优化有符号:
O3+有符号
对比O0和O3的差距非常大
STL内存布局
在调试程序时观察数据内存布局推断容器类型,以64位gnu C++的stl为例
std::string 结构大致如下:
struct basic_string
{
char *begin_; //actual string data
size_t size; //actual size
union
{
size_t capacity; //used if larger than 15 bytes
char sso_buffer[16];//used if smaller than 16 bytes
}
}
该结构体固定长度为32字节,4个dq
如果字符串长度小于16,数据就存储在当前结构,否则重新分配内存存放数据
std::vector 内存布局
struct vector_point
{
Point* start;//指向数据起始长度
Point* end;//指向数据结束地址
Point* max;//指向已分配内存的最大长度
}
固定长度24字节,3个dq
调试验证
std::map内存布局,stl内部使用红黑树实现std::map,要获得所有键值对,这里只需要遍历二叉树即可
struct std::map
{
void* allocator; //ignore
color color; //ignore
node * root;
node * leftmost; //ignore
node * rightmost; //ignore
size_t node_count;
}
struct node
{
color color;
node * parent;
node * left;
node * right;
TypeKey key; //data_area
TypeValue value;
}
解析脚本
import idautils
import idaapi
import idc
# parse gnu c++ stlmap
def parse_gnu_map_header(address):
root = idc.read_dbg_qword(address + 0x10)
return root
def parse_gnu_map_node(address):
left = idc.read_dbg_qword(address + 0x10)
right = idc.read_dbg_qword(address + 0x18)
data = address + 0x20
return left, right, data
def parse_gnu_map_travel(address):
# address <- std::map struct address
result = []
worklist = [parse_gnu_map_header(address)]
while len(worklist) > 0:
addr = worklist.pop()
(left, right, data) = parse_gnu_map_node(addr)
if left > 0:
worklist.append(left)
if right > 0:
worklist.append(right);
result.append(data)
return result
# example
elements = parse_gnu_map_travel(0x564D240D0EB0)
for elem in elements:
print(hex(elem))
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jaytp@qq.com