FlatBuffers小记

IoT 8个月前 admin
103 0 0




逻辑分析


首先运行程序,直接挑明了它用的是flatbuffer:


FlatBuffers小记


静态分析,读取函数为sub_1100,读取之后由sub_18B0和sub_1970进行解析:


FlatBuffers小记


解析出来的对象(?)为v33,结构暂时未知,之后有很多函数从v33中解析数据:


FlatBuffers小记





数据结构逆向


猜测应该和protobuf差不多,先根据flatbuf的语法定义一个数据结构,并在某个语言中通过这个数据结构生成对象,再把对象进行序列化,输入到这个程序中进行反序列化得到原来的对象,然后再通过这些函数来取出原来的对象中的数据。所以只需要还原出来数据结构即可。


函数sub_1BE0明显就是取出了一个字符串,程序根据这个字符串进行相应的操作,可以将其重命名为get_operation。


在create操作中,首先sub_1C00从v33中取出了v32,暂时看不出来有什么作用。然后sub_1C30从v33中取出了size,用于后面的malloc操作,所以可以确定sub_1C30为get_size。


v5和v6是一个东西,都是sub_1C60函数从v33中取出来的,为了搞清楚他是个什么东西,需要先看一下函数sub_1C80和函数sub_1CA0,函数sub_1C80直接对v5进行了解引用,得到了一个__int64,并与v32进行比较;sub_1CA0就比较明显了,直接告诉了我们它是干什么用的:


FlatBuffers小记


现在搞清楚了,v5和v6是一个flatbuffers中的vector,v32是vector的索引。通过简单的分析得出flatbuffers中的vector前四个字节为长度,后面为数据。现在可以确定sub_1C60为Get_vec,sub_1C00为Get_vecidx,sub_1C80为vec_size,sub_1CA0为Get_value_from_vec。


FlatBuffers小记


接下来函数sub_1D10又从value_from_vec中取出了v30,由此可以推断出value_from_vec也是个对象。v30被用作了idx往heaplist里存储申请的堆块和堆块的size,所以sub_1D10为Get_idx。至此create中所有从对象中取值的操作分析完毕。点开每个Get函数会看到里面还套了一层函数,里面的那一层函数第二个参数都是数字,而且对同一个对象操作的不同函数,这个数字都不相同,Get_opeartion里为4、Get_vecidx里为6、get_size里为8、Get_vec里为10:


FlatBuffers小记

FlatBuffers小记

推测数字和每个成员在对象中的偏移有关。整合现有信息即可得出大概的数据结构:


字符串 operation
整数 vecidx
整数 size
Vector vec


接下来要先搞清楚flatbuffer中的vector代表什么,查阅文档可知,在flatbuffer中定义的数组结构,在c++中实现的时候会变成vector,并且string是由byte数组实现的(也就是flatbuffer中定义的string在c++中实现的时候也是vector):


FlatBuffers小记


并且根据文档可以找到flatbuffer中的数据类型,原来的数据结构可以写成:


table Heapinfo{
idx:ulong;
......
}
table Player {
operation:string;
vectoridx:ulong;
create_size:ulong;
vec:[Heapinfo];
}

之所以还有省略号是因为还没分析完(Edit操作中还有新的函数)


分析edit操作,edit中使用Get_value_from_vec从vec中取出对象后除了使用了Get_idx函数获得对象中的idx,又使用了sub_1D40函数获取了对象中的未知数据v26:


FlatBuffers小记


要想知道v26的身份就得先分析函数sub_1D60和sub_1D80,查看sub_1D60发现是对v26进行了解引用得到一个int,和vec_size一样的操作,所以v26不出意外也是个vector。sub_1D80函数返回了v26+4位置的指针,下面memcpy直接用这个指针的数据对堆块进行了编辑,前面我们分析得到flatbuffers中的vector前四个字节为长度,后面为数据,所以v26确实是vector而且是byte数组,那byte数组不就是string吗。


所以可以得到完整的数据结构:


table Heapinfo{
idx:ulong;
heap:string;
}
table Player {
operation:string;
vectoridx:ulong;
create_size:ulong;
vec:[Heapinfo];
}
root_type Player;




验证数据结构


github搞一份源码编译得到flatc,新建文件heap.fbs写入上面得到的数据结构,使用如下命令生成头文件:


./flatc --cpp ./heap.fbs --gen-object-api --gen-mutable

tips:应该先根据猜测的数据结构在C++中生成对象,然后将对象序列化再反序列化之后,根据题目的操作分别对对象中的成员进行取值,编译为二进制文件之后拉下来放到ida里验证那些取值操作和题目里的操作是不是一致。(原来写的代码被我删了,懒得演示了.jpg)


写test.cpp:


#include "flatbuffers/flatbuffers.h"
#include "heaps_generated.h"
#include <iostream>
#include <unistd.h>
int main(){
flatbuffers::FlatBufferBuilder builder;
auto heap = Heap(0x20);
int idx = 0;
auto heapinfo = CreateHeapinfo(builder,idx,&heap);
auto operation = builder.CreateString("Create");
int vecidx = 0;
std::vector<flatbuffers::Offset<Heapinfo>> Heapvec;
Heapvec.push_back(heapinfo);
auto vec = builder.CreateVector(Heapvec);
auto s_player = CreatePlayer(builder,operation,vecidx,0x20,vec);
builder.Finish(s_player);
auto output = builder.GetBufferPointer();
auto output_size = builder.GetSize();
write(1,output,output_size);
std::cout << "finish!" << std::endl;
}


使用如下命令编译:


CPLUS_INCLUDE_PATH="<flatbuffers源码路径>/include" g++ test.cpp -o test


编写脚本用来中转序列化数据:


from pwn import *
context.log_level="debug"
gen=process("./test")
payload=gen.recvuntil(b'finish!',drop=True)
sh=process("../rpg")
sh.sendlineafter(b"Enter flat buffer:",payload)
sh.interactive()


交互成功:


FlatBuffers小记





总结


对于flatbuffer没找到像protobuf一样的现成的逆向工具,头铁硬猜。




FlatBuffers小记


看雪ID:/x01

https://bbs.kanxue.com/user-home-929564.htm

*本文为看雪论坛优秀文章,由 /x01 原创,转载请注明来自看雪社区

FlatBuffers小记

# 往期推荐

1、在 Windows下搭建LLVM 使用环境

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

5、一个简单实践理解栈空间转移

6、记一次某盾手游加固的脱壳与修复


FlatBuffers小记


FlatBuffers小记

球分享

FlatBuffers小记

球点赞

FlatBuffers小记

球在看

原文始发于微信公众号(看雪学苑):FlatBuffers小记

版权声明:admin 发表于 2023年8月31日 下午6:09。
转载请注明:FlatBuffers小记 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...