def extract(self): try: content = open(self.filename,'rb').read() # search all '.proto' strings protos = [] stream = content while len(stream)>0: try: r = stream.index('.proto') for j in range(64): try: if decode_varint128(stream[r-j:])[0]==(j+5) and is_valid_filename(stream[r-j+1:r+6]): # Walk the fields and get a probable size walker = ProtobufFieldsWalker(stream[r-j-1:]) walker.walk() probable_size = walker.get_size() """ Probable size approach is not perfect, we add a delta of 1024 bytes to be sure not to miss something =) """ for k in range(probable_size+1024, 0, -1): try: fds = FileDescriptorProto() fds.ParseFromString(stream[r-j-1:r-j-1+k]) protos.append(stream[r-j-1:r-j-1+k]) print('[i] Found protofile %s (%d bytes)' % (stream[r-j+1:r+6], k)) break except DecodeError: pass except UnicodeDecodeError: pass break except IndexError: pass stream = stream[r+6:] except ValueError: break # Load successively each binary proto file and rebuild it from scratch seen = [] for content in protos: try: # Load the prototype fds = FileDescriptorProto() fds.ParseFromString(content) res = FileDescriptorDisassembler(fds) if len(res.desc.name)>0: if res.desc.name not in seen: open(res.desc.name+'.protoc','wb').write(content) res.render() seen.append(res.desc.name) except DecodeError: pass except IOError: print('[!] Unable to read %s' % sys.argv[1])
def _add_file_from_response( self, file_descriptor: FileDescriptorResponse) -> None: protos: List[bytes] = file_descriptor.file_descriptor_proto for proto in protos: desc = FileDescriptorProto() desc.ParseFromString(proto) if desc.name not in self._known_files: self._logger.info("Loading descriptors from file: %s", desc.name) self._known_files.add(desc.name) self.Add(desc)
async def test_file_by_filename_response(channel): r1, r2 = await ServerReflectionStub(channel).ServerReflectionInfo([ ServerReflectionRequest(file_by_filename=DESCRIPTOR.name, ), ServerReflectionRequest(file_by_filename='my/missing.proto', ), ]) proto_bytes, = r1.file_descriptor_response.file_descriptor_proto dummy_proto = FileDescriptorProto() dummy_proto.ParseFromString(proto_bytes) assert dummy_proto.name == DESCRIPTOR.name assert dummy_proto.package == DESCRIPTOR.package assert r2 == ServerReflectionResponse(error_response=ErrorResponse( error_code=5, error_message='not found', ), )
async def test_file_containing_symbol_response(channel): r1, r2 = await ServerReflectionStub(channel).ServerReflectionInfo([ ServerReflectionRequest(file_containing_symbol=( DESCRIPTOR.message_types_by_name['DummyRequest'].full_name), ), ServerReflectionRequest(file_containing_symbol='unknown.Symbol', ), ]) proto_bytes, = r1.file_descriptor_response.file_descriptor_proto dummy_proto = FileDescriptorProto() dummy_proto.ParseFromString(proto_bytes) assert dummy_proto.name == DESCRIPTOR.name assert dummy_proto.package == DESCRIPTOR.package assert r2 == ServerReflectionResponse(error_response=ErrorResponse( error_code=5, error_message='not found', ), )
def walk_binary(binr): if type(binr) == str: with open(binr, 'rb') as fd: binr = fd.read() # Search for: # ".proto" or ".protodevel", as part of the "name" (1) field cursor = 0 while cursor < len(binr): cursor = binr.find(b'.proto', cursor) if cursor == -1: break cursor += len('.proto') cursor += (binr[cursor:cursor + 5] == b'devel') * 5 # Search back for the (1, length-delimited) marker start = binr.rfind(b'\x0a', max(cursor - 1024, 0), cursor) if start > 0 and binr[start - 1] == 0x0a == (cursor - start - 1): start -= 1 # Check whether length byte is coherent if start == -1: continue varint, end = _DecodeVarint(binr, start + 1) if cursor - end != varint: continue # Look just after for subsequent markers tags = b'\x12\x1a\x22\x2a\x32\x3a\x42\x4a\x50\x58\x62' if binr[cursor] not in tags: continue while cursor < len(binr) and binr[cursor] in tags: tags = tags[tags.index(binr[cursor]):] varint, end = _DecodeVarint(binr, cursor + 1) cursor = end + varint * (binr[cursor] & 0b111 == 2) # Parse descriptor proto = FileDescriptorProto() proto.ParseFromString(binr[start:cursor]) # Convert to ascii yield descpb_to_proto(proto)
def reconstitute_file_from_bytes(self, file_descriptor_proto_bytes): """ Reconstitutes one or more Python protobuf classes from a byte stream. The intended purpose of this function is to create a set of Protobuf Python classes from a byte stream file sent from another service. This way, services can define arbitrary data types and send schemas for those types to other services. Args: file_descriptor_proto_bytes: Serialized protocol buffer file containing one or more messages. Returns: An array containing each class contained in file_descriptor_proto_bytes. """ file_descriptor_proto = FileDescriptorProto() file_descriptor_proto.ParseFromString(file_descriptor_proto_bytes) return self.reconstitute_file(file_descriptor_proto)
def disassemble(self): """Disassemble serialized protocol buffers file. """ ser_pb = open(self.input_file, 'rb').read() # Read serialized pb file fd = FileDescriptorProto() fd.ParseFromString(ser_pb) self.name = fd.name self._print('// Reversed by pbd (https://github.com/rsc-dev/pbd)') if len(fd.package) > 0: self._print('package {};'.format(fd.package)) self.package = fd.package else: self._print('// Package not defined') self._walk(fd)
def update_message_classes(): global message_classes, descriptor_path, method_info factory = MessageFactory() # Add well-known types first for file_descriptor in file_descriptors.values(): file_proto = FileDescriptorProto() file_proto.ParseFromString(file_descriptor.serialized_pb) factory.pool.Add(file_proto) # Then add our types with open(descriptor_path, 'rb') as f: fileset = google.protobuf.descriptor_pb2.FileDescriptorSet.FromString(f.read()) for file_proto in fileset.file: factory.pool.Add(file_proto) message_classes = factory.GetMessages([file_proto.name for file_proto in fileset.file]) # HACK to add nested types. Is there an API for this? for desc in factory.pool._descriptors.values(): if desc.full_name not in message_classes: message_classes[desc.full_name] = factory.GetPrototype(desc) method_info = {} for file_proto in fileset.file: for service in file_proto.service: for method in service.method: k = "{}.{}".format(service.name, method.name) input_type = method.input_type output_type = method.output_type if input_type.startswith('.'): input_type = input_type[1:] if output_type.startswith('.'): output_type = output_type[1:] if input_type not in message_classes or output_type not in message_classes: print("WARNING: types for method {} not found".format(k)) input_type = message_classes[input_type] output_type = message_classes[output_type] method_info[k] = (method, input_type, output_type)