首页
社区
课程
招聘
[原创]FlatBuffers小记
2023-8-16 00:01 9566

[原创]FlatBuffers小记

2023-8-16 00:01
9566

逻辑分析

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

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

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

数据结构逆向

猜测应该和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就比较明显了,直接告诉了我们它是干什么用的:

现在搞清楚了,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。

接下来函数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:

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

1
2
3
4
字符串 operation
整数 vecidx
整数 size
Vector vec

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

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

1
2
3
4
5
6
7
8
9
10
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:

要想知道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吗

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

1
2
3
4
5
6
7
8
9
10
11
table Heapinfo{
  idx:ulong;
  heap:string;
}
table Player {
  operation:string;
  vectoridx:ulong;
  create_size:ulong;
  vec:[Heapinfo];
}
root_type Player;

验证数据结构

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

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

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

写test.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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;
}

使用如下命令编译:

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

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

1
2
3
4
5
6
7
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()

交互成功:

总结

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


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2023-8-16 00:15 被/x01编辑 ,原因: 添加附件
上传的附件:
收藏
点赞1
打赏
分享
最新回复 (2)
雪    币: 4016
活跃值: (5833)
能力值: ( LV7,RANK:102 )
在线值:
发帖
回帖
粉丝
fjqisba 2023-8-16 07:59
2
0
flatbuffer写逆向工具可能会比较难,貌似是因为不知道索引到的地址对应的是什么类型的数据
雪    币: 19410
活跃值: (29069)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-8-16 09:13
3
0
感谢分享
游客
登录 | 注册 方可回帖
返回