Decompiling Flacuffers案例研究:Facebook的GraphQL架构(2020)

2021-04-22 11:51:17

回到2017年,同时寻找facebook的开源客户端,我偶然发现了fbchat.a项目,它为facebook实现了一个开源接口,而#39; s messenger.knows facebook没有正式公开该界面进行用户到用户通信,我变得兴起。从Libpuleplple'它必须使用mqtt,但这不是案例。这一实现使用了基于休息的API,挖掘通过我发现它使用了GraphQL API版本的源代码。

在互联网上挖掘一点之后,我意识到该特定API的公共文档可能是偶尔的博客文献,偶尔有关少数字段或方法。当然,FBCHAT项目管理到挖掘。

可以找到所提供文件和解码模式的源代码:fb-graphql-schema

但是API接口必须公开,至少由这些API的客户端。在这里开始我们的旅程。及其第一批任务,为Facebook找到一个客户' s graphql api.an轻松任务您说?鉴于我们都知道有关Android和Messenger应用程序的Facebook,甚至Facebook Lite,它应该是简单的。不幸的是,我们不知道以什么格式存储了模式,它可以压缩,混淆,它可能只存在于此字节代码。

我们有一个问题,我们如何找到它?这个问题我们可以至少通过简单的启发式回答,这一模式必须包含类型和字段名称。对于我们的一些人已经由FBChat项目提供了一些类型和字段。 。我们最终通过Facebook' s apps.sample graphql查询最终找到了一个字符串搜索,可以在_graphql.py中找到

我更熟悉Android,所以这是我的首选平台。我的首要尝试是Facebook Lite,但我没有成功。我没有成功.Next Up,Facebook for Android(Aka Com.facebook.katana)。我们要去的版本讨论是v230.0.0.36.117,因为我在2019年重新审视了这一架构。

在这种情况下,Apktool D -R给了我一个体面的开始。由于APKTOOL中的错误,需要-R标志。它会禁用资源的提取,但它会影响讨论。

现在,我们可以开始寻找字符串。我们需要一个足够罕见的字符串,以便它' t匹配太多文件,长的是最好的。所以让' s try grep -r lightweight_event_creator 。更肯定,我们得到:

我期待几个月的DEX逆向工程,但任务似乎是一个不同的。我们现在必须推断文件类型。让'试试:

所以它' s一个自定义编码,它可能是ASN.1,专有序列化,或其他任何东西。这档案必须在某处读取,因此我们可能会发现对它的引用,并希望其格式有限:

没什么用的...代码可以从Internet被混淆或下载.Android安装可能会提供更多信息。'查找它的位置和#39; s使用的地方

$ adb root $ adb shellgeneric_arm64:/#setenforce 0generic_arm64:/#stopgeneric_arm64:/#startgeneric_arm64:/#strace -ff -i-s 500 -o / data/local/tmp/strace.zygote -p $(pidof zygote)generic_arm64 :/#grep -r graph_metadata.bin /data/local/tmp/strace.zygote.*strace.zygote.6690:[e893a7ac] praead64(31,"资产/ graph_metadata.bin&#34 ;,25,5584953 )= 25.

在这种情况下,我们一无所获或也许。但它并非开放,也不是Openat。我们有一个偏移偏移量,'■使用APK检查:

所以,它'我们现在有一个提示告诉GDB什么条件打破:

$ adb转发tcp:4444 tcp:4444 $ adb shellgeneric_arm64:/ #am set-debug-app com.facebook.katanageNeric_arm64:/#setprop wrap.com.facebook.katana' gdbserver localhost:4444'

#0 0xf5b567a4在pread64()中,来自目标:/system/lib/libc.so [36/1861]#1 0xf6a768d8在?? ()来自目标:/system/lib/libandroidfww.so#2 0xf6a73190在android :: zipfilero :: findentrybyname(char const *)const()来自目标:/system/lib/libandroidfw.so#3 0xf6a65664在Android :: AssetManager :: OpenNONASSetinpathLocked(Char Const *,Android :: Asset :: AccessMode,AssetManager :: Asset_Path Const&)()来自目标:/system/lib/libandroidfw.so#4 0xf6a65bd6在Android :: AssetManager :: Open(Char Const *,Android :: Asset :: AccessMode)()来自目标:/system/lib/libandroidfw.so#5 0xf753c79e在target:/system/lib/libandroid.so#6 0xda7f928a in ansasset manager_open() ? ()来自目标:/data/data/com.facebook.katana/lib-xzs/libcoldstart.so#7 0xda7f91ec在?? ()来自目标:/data/data/com.facebook.katana/lib-xzs/libcoldstart.so#8 0xda7f9160在?? ()来自目标:/data/data/com.facebook.katana/lib-xzs/libcoldstart.so#9 0xda815bf0在?? ()来自目标:/data/data/com.facebook.katana/lib-xzs/libcoldstart.so#10 0xda6efece在?? ()来自目标:/data/data/com.facebook.katana/lib-xzs/libcoldstart.so#11 0xda6f2186在?? ()来自目标:/data/data/com.facebook.katana/lib-xzs/libcoldstart.so#12 0xda6f2012在?? ()来自目标:/data/data/com.facebook.katana/lib-xzs/libcoldstart.so

看起来像是libcoldstart.so的某个地方'让' s看我们是否能找到它的格式..我们可以通过查看它链接的图书馆来获得一些信息?

$ adb pull / data/data/com.facebook.katana/lib-xzs/libcoldstart.so$ readelf -a libcoldstart.so | Grep'共享库:' 0x00000001(所需)共享库:[libz.so] 0x00000001(所需)共享库:[libdl.so] 0x00000001(所需)共享库:[liblowlevel.so] 0x00000001(需要)共享库:[libxplat_yoga_util_utilandroid.so] 0x00000001(所需的)共享库:[liblog.so] 0x00000001(需要)共享库:[libandroid.so] 0x00000001(所需)共享库:[libxplat_third-party_jsoncpp_jsoncppandroid.so] 0x00000001(所需)共享库:[libglog.so] 0x00000001(需要)共享库:[libfmt.so] 0x00000001(需要)共享库:[libflatbudeersflatc.so] 0x00000001(所需)共享库:[libsigmux.so] 0x00000001(所需)共享库:[libfbsystrace.so] 0x00000001(需要)共享库:[libmemalign16.so] 0x00000001(需要)共享库:[libbreakpad.so] 0x00000001(所需)共享库:[libprofiloextapi.so] 0x00000001(所需)共享库:[Liblinkerutilsmerged.so] 0x00000001(所需)共享库:[libdistractmerged.so] 0x00000001(需要)共享库:[libgnustl _shared.so] 0x00000001(需要)共享库:[libm.so] 0x00000001(所需)共享库:[libc.so]

现在我们对FlinBuffers有一个参考:" libflatbuffersflatc.so"这可能是格式吗?让' s检查。一种简单的方法来看看使用flobuffers是否形成文件的方法是检查首次将4个字节解释为int32_t指向表,表中的第一个Int32_t在Thenegative偏移到其虚拟表中。让' s pee:

$ hexdump -c graph_metadata.bin | HEAD00000000 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 28 00 04 00 | ...........(... | 00000010 08 00 0C 00 10 00 14 00 1 1C 00 20 00 2 00 | ............。$。| 00000020 16 00 00 00 00 00 00 00 e010 2f 00 10 27 25 00 04 3a 1f 00 | ......'%..:.. | 00000030 20 FD 1D 00 AC 96 13 00 94 96 13 00 68 12 13 00 | ........... H ... | 00000040 B4 0E 0B 00 04 00 00 00 64 52 00 00 A0 0B 00 | ........博士。 ..... | 00000050 90 0e 0b 00 84 0e 0b 00 78 0e 0b 00 68 0e 0b 00 | ........ x ... h ... | 00000060 4c 0e 0b 00 28 0e 0b 00 08 0E 0B 00 E4 0D 0B 00 | L ...(........... | ........... | 00000070 C8 0D 0B 00 AC 0D 0B 00 94 0D 0B 00 84 0D 0B 00 | ...... .......... | 00000080 68 0D 0B 00 58 0D 0B 00 40 0D 0B 00 24 0D 0B 00 | H ... X ... ... @ ... $ ... | 00000090 08 0d 0b 00 F0 0C 0B 00 D8 0C 0B 00 C0 0C 0B 00 | ................ |

FlatBuffers中的整数是小endian。我们得到:第一个偏移是0x20,它指向0x16,它指向(0x16,0x28).Assuming 0x16是虚拟表的长度,它在它是Root table之后的数据。似乎它通过了这种启发式。

>>>进口FBS>>> data =打开(' graph_schema.bin'' rb')。read()>>> root = root = fbs.deref_offset(数据,00)>>> fbs.get_table_vt(数据,root){' vt_len&#39 ;:22,' tbl_len&#39 ;: 40,'条目&#39 ;:(4,8,12,16,20 ,24,28,32,36)}}

从这里它'苏猜起作用,以及试验和错误。所以我' ve得到了以下架构,它' s不完整,但它为我们提供了足够的信息来重建所有类型和字段GraphQL API:

架构与你一样。Ve可能注意到有一个额外的编码层,可能会减少它的尺寸。我们不知道什么类型的类型也意味着什么。最有用的指标是,如果差异的类型是短暂的,我们可以通过检查所有值小于向量&#39来检查矢量中的索引'' SLENG和最大索引接近载体的长度。

我们' ll跳过手动解码。我' ll只是向您展示我写入的脚本以从架构中提取类型:

我向Facebook提交了我的调查结果,他们的回应是模式不是保密的。