Skip to content

utils

Utilities functions

check_hash(hash_proto, file_path)

Check if the hash is valid

This method computes the appropriate hash based on what is available in the export file and compare them.

Parameters:

Name Type Description Default
hash_proto Hash

Protobuf message containing the hash

required
file_path Path

Path to the binary

required

Returns:

Type Description
bool

Boolean for success

Source code in bindings/python/quokka/utils.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def check_hash(hash_proto: Pb.Meta.Hash, file_path: pathlib.Path) -> bool:
    """Check if the hash is valid

    This method computes the appropriate hash based on what is available in the export
    file and compare them.

    Arguments:
        hash_proto: Protobuf message containing the hash
        file_path: Path to the binary

    Returns:
        Boolean for success
    """
    hash_methods = {
        Pb.Meta.Hash.HASH_MD5: md5_file,
        Pb.Meta.Hash.HASH_SHA256: sha256_file,
    }

    hash_method = hash_methods.get(hash_proto.hash_type)
    if hash_method is None:
        logger.info("Failed to verify hash for file because no hash was provided.")
        return True

    file_hash = hash_method(file_path)
    return file_hash == hash_proto.hash_value

convert_address_size(proto_address_size)

Convert the proto address size to an int value

Parameters:

Name Type Description Default
proto_address_size 'Pb.AddressSizeValue'

Protobuf field

required

Returns:

Type Description
int

An integer value

Raises:

Type Description
ValueError

When the address size is not known

Source code in bindings/python/quokka/utils.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def convert_address_size(
    proto_address_size: "Pb.AddressSizeValue",
) -> int:
    """Convert the proto address size to an int value

    Arguments:
        proto_address_size: Protobuf field

    Returns:
        An integer value

    Raises:
        ValueError: When the address size is not known
    """
    if proto_address_size == Pb.ADDR_32:
        return 32
    if proto_address_size == Pb.ADDR_64:
        return 64

    raise ValueError("Address size not known")

find_register_access(register, access_mode, instructions)

Traverse the list of instructions searching for the first one that access the specified register with the required access mode.

Parameters:

Name Type Description Default
register str

The identifier of the register as a string

required
access_mode AccessMode

The access mode to the register (read or write)

required
instructions Iterable[Instruction]

An iterable of instructions to analyze

required

Returns:

Type Description
Instruction | None

The first instruction that access the register in the specified mode.

Instruction | None

Return None if no such instruction is found.

Source code in bindings/python/quokka/utils.py
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
def find_register_access(
    register: str, access_mode: AccessMode, instructions: Iterable[Instruction]
) -> Instruction | None:
    """Traverse the list of instructions searching for the first one that access
    the specified register with the required access mode.

    Arguments:
        register: The identifier of the register as a string
        access_mode: The access mode to the register (read or write)
        instructions: An iterable of instructions to analyze

    Returns:
        The first instruction that access the register in the specified mode.
        Return None if no such instruction is found.
    """
    r = register.lower()

    for instr in instructions:
        # Retrieve the list of all registers read or written depending on query
        if any(str(op).lower() == r and access_mode in op.access for op in instr.operands):
            return instr
        # Otherwise continue

    return None

get_arch(isa, address_size, is_thumb=False) cached

Convert an isa to an arch.

Parameters:

Name Type Description Default
isa ArchEnum

Instruction set

required
address_size int

Address size

required
is_thumb bool

Is it thumb mode?

False

Returns:

Type Description
Type['QuokkaArch']

A QuokkaArch

Source code in bindings/python/quokka/utils.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
@functools.lru_cache(maxsize=2, typed=True)
def get_arch(
    isa: ArchEnum, address_size: int, is_thumb: bool = False
) -> Type["QuokkaArch"]:
    """Convert an isa to an arch.

    Arguments:
        isa: Instruction set
        address_size: Address size
        is_thumb: Is it thumb mode?

    Returns:
        A QuokkaArch
    """
    mapping = {
        ArchEnum.ARM: {
            32: ArchARM,
            64: ArchARM64,
        },
        ArchEnum.X86: {
            32: ArchX86,
            64: ArchX64,
        },
        ArchEnum.MIPS: {
            32: ArchMIPS,
            64: ArchMIPS64,
        },
        ArchEnum.PPC: {
            32: ArchPPC,
            64: ArchPPC64,
        },
    }

    platform_arch = mapping.get(isa)
    if platform_arch is None:
        return QuokkaArch

    arch = platform_arch.get(address_size, QuokkaArch)

    if arch == ArchARM and is_thumb:
        arch = ArchARMThumb

    return arch

get_isa(proto_isa)

Convert a proto isa to an architecture

Source code in bindings/python/quokka/utils.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def get_isa(
    proto_isa: "Pb.Meta.ISAValue",
) -> ArchEnum:
    """Convert a proto isa to an architecture"""
    mapping = {
        Pb.Meta.PROC_INTEL: ArchEnum.X86,
        Pb.Meta.PROC_ARM: ArchEnum.ARM,
        # Pb.Meta.PROC_DALVIK: # Currenlty not supported by capstone
        Pb.Meta.PROC_PPC: ArchEnum.PPC,
        Pb.Meta.PROC_MIPS: ArchEnum.MIPS,
    }

    return mapping.get(proto_isa, ArchEnum.UNKNOWN)

md5_file(file_path)

Compute the MD5 of a file

Source code in bindings/python/quokka/utils.py
49
50
51
52
53
54
55
56
def md5_file(file_path: pathlib.Path) -> str:
    """Compute the MD5 of a file"""
    md5 = hashlib.md5()
    with open(file_path.as_posix(), "rb") as fd:
        for byte in iter(lambda: fd.read(65535), b""):
            md5.update(byte)

    return md5.hexdigest()

parse_version(version)

Parse the version returning a tuple with the major, minor and patch

Source code in bindings/python/quokka/utils.py
181
182
183
184
185
186
187
188
189
190
def parse_version(version: str) -> tuple[int, int, int]:
    """Parse the version returning a tuple with the major, minor and patch"""

    parsed = tuple(map(int, version.split(".")))
    if len(parsed) != 3:
        raise ValueError(
            f"Version {version} doesn't respect the format MAJOR.MINOR.PATCH"
        )

    return parsed

sha256_file(file_path)

Compute the SHA-256 of a file

Source code in bindings/python/quokka/utils.py
59
60
61
62
63
64
65
66
def sha256_file(file_path: pathlib.Path) -> str:
    """Compute the SHA-256 of a file"""
    sha = hashlib.sha256()
    with open(file_path.as_posix(), "rb") as fd:
        for byte in iter(lambda: fd.read(65535), b""):
            sha.update(byte)

    return sha.hexdigest()