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 quokka/utils.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
def check_hash(hash_proto: quokka.pb.Quokka.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 = {
        quokka.pb.Quokka.Meta.Hash.HASH_MD5: md5_file,
        quokka.pb.Quokka.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 'quokka.pb.Quokka.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 quokka/utils.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def convert_address_size(
    proto_address_size: "quokka.pb.Quokka.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 == quokka.pb.Quokka.ADDR_32:
        return 32
    if proto_address_size == quokka.pb.Quokka.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
reg

The identifier of the register we are targeting, that can either be the capstone register ID (ex: capstone.x86_const.X86_REG_EAX) or the register name (ex: "eax")

required
access_mode RegAccessMode

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 quokka/utils.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def find_register_access(
    register: int | str, access_mode: RegAccessMode, 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:
        reg: The identifier of the register we are targeting, that can either be
            the capstone register ID (ex: capstone.x86_const.X86_REG_EAX) or the
            register name (ex: "eax")
        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.
    """

    for instr in instructions:
        # Retrieve the list of all registers read or modified by the instruction using capstone
        regs_read, regs_write = instr.cs_inst.regs_access()

        # Remap registers to the correct type
        if isinstance(register, str):
            register = register.lower()
            regs_read = [instr.cs_inst.reg_name(r) for r in regs_read]
            regs_write = [instr.cs_inst.reg_name(r) for r in regs_write]

        # Check if it is accessing the target register in the correct mode
        if (
            register in regs_write
            and (access_mode == RegAccessMode.WRITE or access_mode == RegAccessMode.ANY)
        ) or (
            register in regs_read
            and (access_mode == RegAccessMode.READ or access_mode == RegAccessMode.ANY)
        ):
            return instr

    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 quokka/utils.py
131
132
133
134
135
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
@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 quokka/utils.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def get_isa(
    proto_isa: "quokka.pb.Quokka.Meta.ISAValue",
) -> ArchEnum:
    """Convert a proto isa to an architecture"""
    mapping = {
        quokka.pb.Quokka.Meta.PROC_INTEL: ArchEnum.X86,
        quokka.pb.Quokka.Meta.PROC_ARM: ArchEnum.ARM,
        quokka.pb.Quokka.Meta.PROC_PPC: ArchEnum.PPC,
        quokka.pb.Quokka.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 quokka/utils.py
48
49
50
51
52
53
54
55
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 quokka/utils.py
176
177
178
179
180
181
182
183
184
185
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 quokka/utils.py
58
59
60
61
62
63
64
65
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()