-
Notifications
You must be signed in to change notification settings - Fork 9
/
binfile.py
executable file
·149 lines (110 loc) · 4.13 KB
/
binfile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env python3
from collections import namedtuple
from dataclasses import dataclass
from enum import Enum
from typing import Optional
import struct
class Etype(Enum):
IET_END = 0
IET_REL_I0 = 2
IET_IMM_U0 = 3
IET_REL_I8 = 4
IET_IMM_U8 = 5
IET_REL_I16 = 6
IET_IMM_U16 = 7
IET_REL_I32 = 8
IET_IMM_U32 = 9
IET_REL_I64 = 10
IET_IMM_I64 = 11
IET_REL32_EXPORT = 16
IET_IMM32_EXPORT = 17
IET_REL64_EXPORT = 18
IET_IMM64_EXPORT = 19
IET_ABS_ADDR = 20
IET_CODE_HEAP = 21
IET_ZEROED_CODE_HEAP = 22
IET_DATA_HEAP = 23
IET_ZEROED_DATA_HEAP = 24
IET_MAIN = 25
@dataclass(frozen=True)
class Export:
type: Etype
name: bytes
address: int
@dataclass(frozen=True)
class Relocation:
type: Etype
symbol: Optional[bytes]
address: int
CBinFile = struct.Struct("<2sBB4sqqq")
BinFileHeader = namedtuple(
"BinFileHeader",
"jmp module_align_bits reserved bin_signature org patch_table_offset file_size",
)
# Read zero-terminated string. Return string & length including terminator.
def read_string(bytes_slice):
terminator_pos = bytes_slice.find(b"\0")
assert terminator_pos >= 0
# If input is bytearray, make an immutable copy
return bytes(bytes_slice[:terminator_pos]), terminator_pos + 1
def parse_patch_table(binfile, patch_table_offset):
pos = patch_table_offset
relocations = set()
exports = set()
last_etype, last_symbol = None, None
while binfile[pos]:
etype, value = struct.unpack("<BI", binfile[pos : pos + 5])
pos += 5
symbol_name, len_ = read_string(binfile[pos:])
pos += len_
if symbol_name == b"":
symbol_name = None
etype = Etype(etype)
if etype == Etype.IET_ABS_ADDR:
for j in range(value):
(address,) = struct.unpack("<I", binfile[pos : pos + 4])
pos += 4
relocations.add(Relocation(etype, None, address))
elif etype in {Etype.IET_REL32_EXPORT}:
exports.add(Export(etype, symbol_name, value))
elif etype in {Etype.IET_IMM_U32, Etype.IET_REL_I32}:
if symbol_name is None:
assert etype is last_etype and last_symbol is not None
symbol_name = last_symbol
relocations.add(Relocation(etype, symbol_name, value))
elif etype == Etype.IET_MAIN:
assert symbol_name is None
exports.add(Export(etype, symbol_name, value))
else:
raise Exception(f"Unhandled etype {Etype(etype)}")
last_etype, last_symbol = etype, symbol_name
return relocations, exports
def parse(f, verbose=False):
b = f.read(CBinFile.size)
bfh = BinFileHeader._make(CBinFile.unpack(b))
assert bfh.bin_signature == b"TOSB"
assert bfh.module_align_bits >= 0 and bfh.module_align_bits < 64
module_align = 1 << bfh.module_align_bits
if verbose:
print("BIN header:")
print(f" jmp [{bfh.jmp[0]:02X} {bfh.jmp[1]:02X}]h")
print(f" alignment {module_align} byte(s)")
print(f" org {bfh.org:016X} ({bfh.org})")
print(f" patch_table_offset {bfh.patch_table_offset:016X} ({bfh.patch_table_offset})")
print(f" file_size {bfh.file_size:016X} ({bfh.file_size})")
print()
image_size = bfh.file_size - CBinFile.size
image = bytearray(f.read(image_size))
assert len(image) == image_size
relocations, exports = parse_patch_table(image, bfh.patch_table_offset - CBinFile.size)
return image, relocations, exports
if __name__ == "__main__":
import argparse
from pathlib import Path
parser = argparse.ArgumentParser()
parser.add_argument("binfile", type=Path)
args = parser.parse_args()
print("binfile", args.binfile.name)
print()
with open(args.binfile, "rb") as f:
image, relocations, exports = parse(f, verbose=True)