
🕵️ 알 수 없는 정의를 가진 Protocol Buffers를 리버스 엔지니어링하는 도구
Google Protobuf로 인코딩된 blob(버전 2 또는 3)을 해당 정의 없이도 파싱할 수 있는 간단한 프로그램입니다. blob의 내용을 보기 좋고 컬러로 표현해 줍니다. 예시:

보시다시피 필드 이름은 당연히 사라지며, 다음과 같은 상세 정보도 함께 사라집니다:
하지만 protobuf-inspector는 대부분의 경우 메시지 구조를 올바르게 추측할 수 있습니다. 필드에 임베디드 바이너리 데이터가 발견되면, 먼저 해당 데이터를 메시지로 파싱하려고 시도합니다. 실패하면 문자열 또는 헥스덤프로 데이터를 표시합니다. 작은 청크에서는 특히 실수할 수 있습니다.
필드가 와이어에 인코딩된 순서대로 표시되므로, 와이어 형식에 익숙해지려는 사람이나 파서 개발자, 그리고 리버스 엔지니어링에 유용할 수 있습니다.
pip로 설치할 수 있습니다:
pip install protobuf-inspector
이 명령은 protobuf_inspector 명령어를 설치합니다. 실행할 때 protobuf blob을 stdin으로 전달합니다:
protobuf_inspector < my-protobuf-blob
blob의 첫 번째 (맹목적인) 분석 결과를 확인한 후, 일반적으로 필드 중 일부를 정의하기 시작하여 protobuf-inspector가 blob을 더 잘 파싱할 수 있도록 하며, 최종적으로 완전한 protobuf 정의를 가지고 파서가 더 이상 추측할 필요가 없는 지점에 도달합니다.
파싱 오류가 발견되면 해당 필드 내에서 파싱이 중단되지만, 계층 구조 외부에서는 영향을 받지 않고 계속 진행됩니다. 해당 필드 내용이 있어야 할 위치에 스택 트레이스가 출력되며, 해당 청크에서 파싱이 중단된 위치를 나타내는 헥스덤프도 함께 표시됩니다(해당되는 경우).
따라서 uint32를 지정했는데 더 큰 varint가 발견되면 다음과 같이 표시됩니다:

어떤 필드에 임베디드 메시지가 포함되어 있다고 지정했지만, 해당 위치에 유효하지 않은 데이터가 발견되면 다음과 같이 표시됩니다:

하나 이상의 파싱 오류가 발생하면 main.py가 0이 아닌 상태 코드로 종료된다는 점에 유의하세요.
blob을 분석할 때 시간을 절약할 수 있는 몇 가지 팁이 있습니다:
varint가 zig-zag 인코딩을 사용하지 않는다고 확신하지만 여전히 부호가 확실하지 않다면, varint로 남겨두세요. zig-zag 인코딩을 사용한다면, 32비트인지 64비트인지 확실하지 않은 경우 sint64를 사용하세요.
어떤 청크가 packed chunk 또는 임베디드 메시지로 잘못 인식되거나, 파싱된 메시지에 이상한 점이 있어 원시 바이트를 보고 싶다면, 타입을 bytes로 지정하세요. 반대로 어떤 이유로 임베디드 메시지로 감지되지 않아야 하는데 감지된다면, 강제로 message로 설정하여 이유를 확인하세요.
청크의 원시 데이터를 파일로 추출하여 더 자세히 분석하고 싶다면, 타입을 dump로 지정하면 protobuf-inspector가 일치하는 blob을 찾을 때마다 dump.0, dump.1 등을 생성합니다.
protobuf-inspector는 blob을 root 타입의 메시지로 파싱하지만, 이는 기본값일 뿐입니다. 많은 메시지 타입이 정의되어 있다면, 선택적 인수로 타입 이름을 전달할 수 있으며, protobuf-inspector는 root 대신 해당 타입을 사용합니다:
protobuf_inspector request < my-protobuf-blob
간단한 예제:
from protobuf_inspector.types import StandardParser
parser = StandardParser()
with open('my-blob', 'rb') as fh:
output = parser.parse_message(fh, "message")
print(output)
이 프로젝트는 처음에 라이브러리로 사용되도록 설계되지 않았으므로, API가 변경될 수 있습니다. 더 복잡한 예제는 protobuf_inspector/__main__.py를 참조하세요.