如何剥离二进制文件中的Protobuf结构名称防范应用逆向工程
从二进制文件中剥离Protobuf结构名、字段名的实现方法
这些内嵌在二进制里的字段名、结构名来自Protobuf默认生成的完整描述符集与反射支持代码,而Protobuf通信序列化/反序列化过程完全依赖字段编号(Tag)完成,不需要任何字段名称信息,只要没有基于字段名的反射调用逻辑,以下方法都可以安全移除相关信息,不影响正常通信:
1. 优先使用Protobuf Lite运行时编译
这是最彻底、最稳定的方案,Lite版本运行时专门裁剪了完整描述符、反射能力,从根源上不会把字段名、结构名等元数据编进二进制:
- 编译.proto文件时,在文件头部添加配置:
option optimize_for = LITE_RUNTIME; - 不同语言编译时对应选择Lite输出参数:C++使用
--cpp_out=lite:指定输出路径,Java/Go/C#等语言直接切换依赖到对应版本的protobuf-lite库即可,业务层序列化/反序列化代码不需要做任何修改。
2. 链接阶段裁剪未引用的元数据段
如果因为业务原因必须使用完整版Protobuf运行时,可以通过编译链接参数,让链接器自动回收没有被实际引用的描述符数据:
- Linux/macOS平台使用GCC/Clang编译时,编译阶段加
-fdata-sections -ffunction-sections参数,链接阶段加-Wl,--gc-sections -s -w参数,前者会自动回收无引用的函数、数据段,后者会移除二进制里的符号表、调试信息,未被反射逻辑引用的描述符字符串会被直接清理。 - Windows平台使用MSVC编译时,开启链接选项
/OPT:REF即可自动裁剪未引用数据段,配合/DEBUG:NONE移除调试信息。
3. 必须保留反射能力时的混淆方案
如果业务确实依赖Protobuf反射能力,无法直接移除描述符,可以对生成的描述符数据做混淆:
- 找到生成代码中存储描述符的常量段,把里面的原始结构名、字段名统一替换为无意义的随机短字符串,注意不要修改任何字段编号、字段类型、字段顺序定义。
- 如果你的业务代码里存在通过字段名反射查找字段的逻辑,同步修改对应查找逻辑里的字符串即可,不会影响序列化通信能力。
注意:所有操作都不能修改.proto定义中的字段编号、字段类型、结构嵌套关系,这三类信息是Protobuf协议解析的核心依据,改动会直接导致通信解析失败。字段名、结构名不参与线上编解码流程,只要没有基于名称的反射调用,移除或替换都不会产生功能问题。

内容的提问来源于stack exchange,提问作者Kaspek
相关产品推荐
相关产品推荐

