BLACKBOXPROTOBUF(3)
π BLACKBOXPROTOBUF π
ok so like. this is a manual page from the Python Library Functions section, and bestie, it's a good one. buckle up. π¦
β¨ NAME β¨
blackboxprotobuf β decode and re-encode Protocol Buffer messages without a .proto descriptor. no descriptor? no problem. πβοΈ
πΈ SYNOPSIS πΈ
import blackboxprotobuf
message, typedef = blackboxprotobuf.decode_message(data)
data = blackboxprotobuf.encode_message(message, typedef)
that's it. that's the whole vibe. β¨
π DESCRIPTION π
blackboxprotobuf is a Python module for decoding and re-encoding Protocol Buffer (protobuf) messages when you do not have access to the original .proto descriptor file. it's giving "found this mysterious binary blob, must understand it." π
it's made for reverse engineering and penetration testing situations β you know the ones β where the protobuf definitions are literally not available but you have to inspect or modify the messages anyway. lowkey iconic honestly.
the library gives you two whole ways in:
- a programmatic Python interface π
- a command-line interface (go check out
bbpb(1), i'll wait) π»
π§ BACKGROUND π§
Protocol Buffers are a binary serialization format made by Google. messages get defined in .proto files that both the sender and the receiver share. cute, right?
here's the plot twist though: serialized protobuf messages do not contain field names or complete type information. each field only carries:
- a field number
- a wire type
the wire type defines how the field length gets interpreted β which means unknown fields can still be skipped. slay. π«Ά
there's also the protoc compiler, which has a --decode_raw option for decoding protocol buffers without the definition. but β and this is the important part β it does not support reconstructing and re-encoding modified payloads. so it can look but it can't touch. π
blackboxprotobuf fills that gap. main character energy, fr fr.
π₯ HOW IT WORKS π₯
the library does best-effort type inference based on:
- the wire type
- field content
then it constructs a type definition (a "typedef") that can later be used to re-encode the message. so you decode once, and you keep the keys. π
and because guessing is guessing, users may override the guessed types with custom type definitions β and assign human-readable field names. treat yourself. π
π CORE API π
decode_message(data, typedef=None, config=None)β decodes a protobuf byte string. returns a tuple:(message_dict, generated_typedef). if you don't hand it a typedef, all field types get guessed. rolling the dice bestie π²encode_message(message_dict, typedef, config=None)β encodes a message dictionary using the type definition you provide. heads up: all fields have to be defined in the typedef or it's not happening. no cap.protobuf_to_json(data, typedef=None, config=None)β convenience wrapper so you can decode into JSON-compatible structures. the lazy-girl route, respectfully. πprotobuf_from_json(json_obj, typedef=None, config=None)β encode from a JSON-compatible representation. the other direction. we love a round trip. π
π TYPE DEFINITIONS π
type definitions are Python dictionaries that describe the message structure. each entry works like this:
key= field number (an integer)value= a dictionary containing at minimum:"type": a string type identifier
the valid type identifiers live in:
blackboxprotobuf.lib.types.type_maps
and message fields must also contain one of these two:
"message_typedef""message_type_name"
β οΈ if both are provided, message_type_name gets ignored. only one of them is getting the spotlight.
π JSON CONVENIENCE FUNCTIONS π
the JSON helpers protobuf_to_json and protobuf_from_json automatically:
- sort output fields
- improve readability of bytes
- annotate example values in the typedef
these two are literally designed for user-facing workflows. they're the presentable ones. π
π PROTOFILE IMPORT/EXPORT π
import_protofile(filename)β converts a.protofile into a blackboxprotobuf type definition. glow-up complete. β¨export_protofile(typedef, filename)β exports a type definition out to a.protofile.
limitations (read these, bestie, they matter):
- not a complete protobuf parser
- does not automatically resolve
importstatements - imported files must be manually processed first
β TYPE VALIDATION β
validate_typedef(typedef, previous_typedef=None)β validates the internal consistency of a type definition you've been messing with.
it detects:
- incompatible type changes
- duplicate field names
- structural inconsistencies
basically it's the friend who tells you your outfit has a stain. helpful. honest. slightly devastating. π
π§ HELPER FUNCTIONS π§
json_safe_transform()β encodes/decodes bytes usinglatin1for readable JSON output.sort_output()β sorts decoded message fields numerically.sort_typedef()β sorts typedef fields for improved readability.
little guys. doing little jobs. obsessed. π«Ά
π CONFIGURATION π
most API functions accept a config parameter of type:
blackboxprotobuf.lib.config.Config
this replaces the earlier global variables. we do not do globals around here anymore, periodt.
config options include:
known_typesβ a mapping of message type names to typedefs.default_binary_typeβ the default type for unknown binary fields. Default:"bytes". Alternatives:"bytes_hex", and a future"bytes_base64". πdefault_typesβ override the default wire-type mappings. example:
default_types[WIRETYPE_VARINT] = "uint"
if no config is provided, the global default configuration object is used. so you always get something.
πΈ WIRE TYPE BREAKDOWN πΈ
π’ Variable Length Integers (varint)
used for:
- integers
- booleans
- enums
supported interpretations:
uintβ unsignedintβ signed (inefficient for negatives)sintβ ZigZag encoding (efficient negatives)
default: int
π Fixed32 / Fixed64
fixed-size integers or floating point values. default interpretation: floating point. π«§
π¦ Length Delimited
used for:
- strings
- bytes
- nested messages
- packed repeated fields
default for unknown binary fields: string/bytes.
β οΈ packed repeated fields are difficult to distinguish from arbitrary byte strings and may require manual correction. it's not a flaw, it's a character trait. π
π PACKED REPEATED FIELDS π
history lesson, stay with me:
- in protobuf v2, repeated fields had to be explicitly declared packed.
- in protobuf v3, they are packed by default.
and because packed fields omit per-element tags, correct parsing often requires user intervention. so it's a you problem now, but like, lovingly. π
β¨ EXAMPLE β¨
import blackboxprotobuf
import base64
data = base64.b64decode('KglNb2RpZnkgTWU=')
message, typedef = blackboxprotobuf.decode_message(data)
message[5] = "Modified Me"
new_data = blackboxprotobuf.encode_message(message, typedef)
decode it. poke it. change field 5. re-encode it. iconique. π
π SEE ALSO π
π LICENSE π
Expat
Β©οΈ COPYRIGHT Β©οΈ
Copyright (c) 2018-2026 NCC Group Plc
π«Ά MAN PAGE π«Ά
Enkelena Haxhija (EnkelenaH) created this man page based on the upstream README. (we stan a documentation queen π)
and that's a wrap, bestie! this page is part of blackboxprotobuf 1.4.2, dated February 2026 β go decode something weird, you've got this. πβ¨π¦
π¦ where this page comes from
- ubuntu stonking: python3-blackboxprotobuf 1.4.2-2 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms