自从国赛遇到protobuf题目之后就一直想好好研究一下,最近才学习其用法。

0x00 配置环境

VMware Ubuntu22.04
VMware Ubuntu20.04
WSL2 Ubuntu22.04均按照步骤配置环境没有问题

windows环境没有测试

关于protobuf,网上有很多介绍,不多赘述。

0x01 安装

一些注意事项

请确保环境安装了make并且能正常使用,因为项目需要自己编译。

Ubuntu22是默认自带较高版本的protobuf的,如果你不打算两个版本同时存在在电脑上的话,可以选择卸载。(两个版本一起也能用,但比较麻烦,我没研究)

1
2
3
4
5
6
7
8
$ which protoc
# protoc: /usr/bin/protoc(虽然不知道为什么我的实在anaconda下)
rm -rf /usr/bin/protoc #这里的路径是上面显示的路径

sudo rm -rf /usr/include/google/protobuf #头文件
sudo rm -rf /usr/local/include/google/protobuf #头文件
sudo rm -rf /usr/lib/libproto* #库文件
sudo rm -rf /usr/local/lib/libproto* # 库文件

来源

编译安装protoc库

(protoc是protobuf的库,不是protobuf-c的,请注意区分)

1
2
$ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-all-3.6.1.tar.gz
$ tar -xvzf protobuf-all-3.6.1.tar.gz

也可以在浏览器中下载后手动解压。这里下载的版本比较旧了,是为了适配后面要安装的protobuf-c,如果不需要,也可以下载最新版(但是一般做pwn题目前大多都还是C语言写的)。如果只需要C不需要其他语言的,也可以下载只有cpp版本的,可以减小体积和加快编译(但不多)。

1
2
$ cd protobuf-all-3.6.1
$ ./configure && make && sudo make install

其实上面三步建议分开,要是哪一步出问题了还可以去查一查。在install之前可以运行make check检查一下。make需要花点时间,取决于你的电脑性能。编译完之后运行

1
2
3
$ ln -s /usr/local/lib/libprotobuf.so.17 /usr/lib/libprotobuf.so.17
$ ln -s /usr/local/lib/libprotoc.so.17 /usr/lib/libprotoc.so.17
$ sudo ldconfig

这时候输入protoc --version应该就会回显libprotoc 3.6.1,如果回显3.20.x,说明是Ubuntu22原本自带的版本没删干净。

编译安装protobuf-c

protobuf原生并不支持C,这里用一个第三方库来兼容C语言。编译步骤和上面一样。

1
2
3
4
$ wget https://github.com/protobuf-c/protobuf-c/releases/download/v1.5.0/protobuf-c-1.5.0.tar.gz
$ tar -xvzf protobuf-c-1.5.0.tar.gz
$ cd protobuf-c-1.5.0
$ ./configure && make && sudo make install

顺带一提,如果上面protobuf安装的版本过高,那么在configure的时候会提示没找到protobuf。

安装python第三方库

写脚本要用到google对protobuf支持的第三方库。如果没安装,运行脚本时候会显示没有google库。不用像网上把整个google库都下下来。

1
$ pip3 install protobuf==3.20.3

这里protobuf的版本要用到3.20.x(只有1 2 3),如果没指定版本直接安装了最新版,运行脚本的时候python会提示版本不兼容,protobuf版本过低,要更新protobuf,否则使用3.20.x版本的python库。安装完这个之后,环境就算是配置好了。

基础使用方法

-I参数指定proto源码目录,–c_out参数指定生成的类声明与实现文件的输出目录。如果proto文件就在当前目录,直接运行:

1
$ protoc --c_out=. filename.proto

那么文件就会生成在当前目录。如果你想出题和对照着函数实现来逆向,那么这一步是必要的。如果你要用python写脚本,那么你需要生成python的实现代码:

1
$ protoc --python_out=. filename.proto

运行须知

如果程序使用了protobuf,那必定需要libprotobuf动态库的。上述安装的版本下,运行库的名字叫libprotobuf-c.so.1。假如protobuf_demo是一个使用了protobuf的程序

1
2
3
4
5
$ ldd protobuf_demo
linux-vdso.so.1 (0x00007ffcc5597000)
libprotobuf-c.so.1 => /usr/local/lib/libprotobuf-c.so.1 (0x00007f2a99c64000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2a99a3b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2a99c81000)

光有libprotobuf-c.so.1也不够,还需要libprotobuf-c.so.1.0.0

1
2
3
4
5
6
7
8
$ ldconfig -v
...
/usr/local/lib: (from /etc/ld.so.conf.d/libc.conf:2)
libprotobuf-c.so.1 -> libprotobuf-c.so.1.0.0
libprotobuf.so.17 -> libprotobuf.so.17.0.0
libprotobuf-lite.so.17 -> libprotobuf-lite.so.17.0.0
libprotoc.so.17 -> libprotoc.so.17.0.0
...

如果在一个纯净的环境(容器)或者版本不适配(高版本的protobuf动态库名字不一样)的情况下,不想安装完整的protobuf又想要运行程序的,可以把这两个文件放到/usr/local/lib下,并运行

1
2
$ ln -sf /usr/local/lib/libprotobuf-c.so.1.0.0 /usr/local/lib/libprotobuf-c.so.1
$ sudo ldconfig

如果不想破坏原本高版本的protobuf,可以就放在程序目录下,运行上面的命令先链接,然后利用patchelf来修改动态链接库路径。有时候也可能是不知道为什么程序就运行不了的,也可以用这种方式试试。

1
2
$ patchelf --print-needed protobuf_demo #打印程序需要的动态库
$ patchelf --replace-needed libprotobuf-c.so.1 ./libcprotobuf-c.so.1 protobuf #第二个libcproto*是附件给你的或者你下载的动态库的路径

配置容器时同理。

参考

Real返璞归真师傅的全面解析

⬆︎TOP