xref: /llvm-project/llvm/test/tools/llvm-objcopy/MachO/Inputs/code-signature-check.py (revision b71edfaa4ec3c998aadb35255ce2f60bba2940b0)
1a299b247SNuri Amari"""Checks the validity of MachO binary signatures
2a299b247SNuri Amari
3a299b247SNuri AmariMachO binaries sometimes include a LC_CODE_SIGNATURE load command
4a299b247SNuri Amariand corresponding section in the __LINKEDIT segment that together
5a299b247SNuri Amariwork to "sign" the binary. This script is used to check the validity
6a299b247SNuri Amariof this signature.
7a299b247SNuri Amari
8a299b247SNuri AmariUsage:
9a299b247SNuri Amari    ./code-signature-check.py my_binary 800 300 0 800
10a299b247SNuri Amari
11a299b247SNuri AmariArguments:
12a299b247SNuri Amari   binary - The MachO binary to be tested
13a299b247SNuri Amari   offset - The offset from the start of the binary to where the code signature section begins
14a299b247SNuri Amari   size - The size of the code signature section in the binary
15a299b247SNuri Amari   code_offset - The point in the binary to begin hashing
16a299b247SNuri Amari   code_size - The length starting from code_offset to hash
17a299b247SNuri Amari"""
18a299b247SNuri Amari
19a299b247SNuri Amariimport argparse
20a299b247SNuri Amariimport collections
21a299b247SNuri Amariimport hashlib
22a299b247SNuri Amariimport itertools
23a299b247SNuri Amariimport struct
24a299b247SNuri Amariimport sys
25a299b247SNuri Amariimport typing
26a299b247SNuri Amari
27*b71edfaaSTobias Hieta
28a299b247SNuri Amariclass CodeDirectoryVersion:
29a299b247SNuri Amari    SUPPORTSSCATTER = 0x20100
30a299b247SNuri Amari    SUPPORTSTEAMID = 0x20200
31a299b247SNuri Amari    SUPPORTSCODELIMIT64 = 0x20300
32a299b247SNuri Amari    SUPPORTSEXECSEG = 0x20400
33a299b247SNuri Amari
34*b71edfaaSTobias Hieta
35a299b247SNuri Amariclass CodeDirectory:
36a299b247SNuri Amari    @staticmethod
37*b71edfaaSTobias Hieta    def make(
38*b71edfaaSTobias Hieta        buf: memoryview,
39*b71edfaaSTobias Hieta    ) -> typing.Union[
40*b71edfaaSTobias Hieta        "CodeDirectoryBase",
41*b71edfaaSTobias Hieta        "CodeDirectoryV20100",
42*b71edfaaSTobias Hieta        "CodeDirectoryV20200",
43*b71edfaaSTobias Hieta        "CodeDirectoryV20300",
44*b71edfaaSTobias Hieta        "CodeDirectoryV20400",
45*b71edfaaSTobias Hieta    ]:
46a299b247SNuri Amari        _magic, _length, version = struct.unpack_from(">III", buf, 0)
47a299b247SNuri Amari        subtype = {
48a299b247SNuri Amari            CodeDirectoryVersion.SUPPORTSSCATTER: CodeDirectoryV20100,
49a299b247SNuri Amari            CodeDirectoryVersion.SUPPORTSTEAMID: CodeDirectoryV20200,
50a299b247SNuri Amari            CodeDirectoryVersion.SUPPORTSCODELIMIT64: CodeDirectoryV20300,
51a299b247SNuri Amari            CodeDirectoryVersion.SUPPORTSEXECSEG: CodeDirectoryV20400,
52a299b247SNuri Amari        }.get(version, CodeDirectoryBase)
53a299b247SNuri Amari
54a299b247SNuri Amari        return subtype._make(struct.unpack_from(subtype._format(), buf, 0))
55a299b247SNuri Amari
56*b71edfaaSTobias Hieta
57a299b247SNuri Amariclass CodeDirectoryBase(typing.NamedTuple):
58a299b247SNuri Amari    magic: int
59a299b247SNuri Amari    length: int
60a299b247SNuri Amari    version: int
61a299b247SNuri Amari    flags: int
62a299b247SNuri Amari    hashOffset: int
63a299b247SNuri Amari    identOffset: int
64a299b247SNuri Amari    nSpecialSlots: int
65a299b247SNuri Amari    nCodeSlots: int
66a299b247SNuri Amari    codeLimit: int
67a299b247SNuri Amari    hashSize: int
68a299b247SNuri Amari    hashType: int
69a299b247SNuri Amari    platform: int
70a299b247SNuri Amari    pageSize: int
71a299b247SNuri Amari    spare2: int
72a299b247SNuri Amari
73a299b247SNuri Amari    @staticmethod
74a299b247SNuri Amari    def _format() -> str:
75a299b247SNuri Amari        return ">IIIIIIIIIBBBBI"
76a299b247SNuri Amari
77*b71edfaaSTobias Hieta
78a299b247SNuri Amariclass CodeDirectoryV20100(typing.NamedTuple):
79a299b247SNuri Amari    magic: int
80a299b247SNuri Amari    length: int
81a299b247SNuri Amari    version: int
82a299b247SNuri Amari    flags: int
83a299b247SNuri Amari    hashOffset: int
84a299b247SNuri Amari    identOffset: int
85a299b247SNuri Amari    nSpecialSlots: int
86a299b247SNuri Amari    nCodeSlots: int
87a299b247SNuri Amari    codeLimit: int
88a299b247SNuri Amari    hashSize: int
89a299b247SNuri Amari    hashType: int
90a299b247SNuri Amari    platform: int
91a299b247SNuri Amari    pageSize: int
92a299b247SNuri Amari    spare2: int
93a299b247SNuri Amari
94a299b247SNuri Amari    scatterOffset: int
95a299b247SNuri Amari
96a299b247SNuri Amari    @staticmethod
97a299b247SNuri Amari    def _format() -> str:
98a299b247SNuri Amari        return CodeDirectoryBase._format() + "I"
99a299b247SNuri Amari
100*b71edfaaSTobias Hieta
101a299b247SNuri Amariclass CodeDirectoryV20200(typing.NamedTuple):
102a299b247SNuri Amari    magic: int
103a299b247SNuri Amari    length: int
104a299b247SNuri Amari    version: int
105a299b247SNuri Amari    flags: int
106a299b247SNuri Amari    hashOffset: int
107a299b247SNuri Amari    identOffset: int
108a299b247SNuri Amari    nSpecialSlots: int
109a299b247SNuri Amari    nCodeSlots: int
110a299b247SNuri Amari    codeLimit: int
111a299b247SNuri Amari    hashSize: int
112a299b247SNuri Amari    hashType: int
113a299b247SNuri Amari    platform: int
114a299b247SNuri Amari    pageSize: int
115a299b247SNuri Amari    spare2: int
116a299b247SNuri Amari
117a299b247SNuri Amari    scatterOffset: int
118a299b247SNuri Amari
119a299b247SNuri Amari    teamOffset: int
120a299b247SNuri Amari
121a299b247SNuri Amari    @staticmethod
122a299b247SNuri Amari    def _format() -> str:
123a299b247SNuri Amari        return CodeDirectoryV20100._format() + "I"
124a299b247SNuri Amari
125*b71edfaaSTobias Hieta
126a299b247SNuri Amariclass CodeDirectoryV20300(typing.NamedTuple):
127a299b247SNuri Amari    magic: int
128a299b247SNuri Amari    length: int
129a299b247SNuri Amari    version: int
130a299b247SNuri Amari    flags: int
131a299b247SNuri Amari    hashOffset: int
132a299b247SNuri Amari    identOffset: int
133a299b247SNuri Amari    nSpecialSlots: int
134a299b247SNuri Amari    nCodeSlots: int
135a299b247SNuri Amari    codeLimit: int
136a299b247SNuri Amari    hashSize: int
137a299b247SNuri Amari    hashType: int
138a299b247SNuri Amari    platform: int
139a299b247SNuri Amari    pageSize: int
140a299b247SNuri Amari    spare2: int
141a299b247SNuri Amari
142a299b247SNuri Amari    scatterOffset: int
143a299b247SNuri Amari
144a299b247SNuri Amari    teamOffset: int
145a299b247SNuri Amari
146a299b247SNuri Amari    spare3: int
147a299b247SNuri Amari    codeLimit64: int
148a299b247SNuri Amari
149a299b247SNuri Amari    @staticmethod
150a299b247SNuri Amari    def _format() -> str:
151a299b247SNuri Amari        return CodeDirectoryV20200._format() + "IQ"
152a299b247SNuri Amari
153*b71edfaaSTobias Hieta
154a299b247SNuri Amariclass CodeDirectoryV20400(typing.NamedTuple):
155a299b247SNuri Amari    magic: int
156a299b247SNuri Amari    length: int
157a299b247SNuri Amari    version: int
158a299b247SNuri Amari    flags: int
159a299b247SNuri Amari    hashOffset: int
160a299b247SNuri Amari    identOffset: int
161a299b247SNuri Amari    nSpecialSlots: int
162a299b247SNuri Amari    nCodeSlots: int
163a299b247SNuri Amari    codeLimit: int
164a299b247SNuri Amari    hashSize: int
165a299b247SNuri Amari    hashType: int
166a299b247SNuri Amari    platform: int
167a299b247SNuri Amari    pageSize: int
168a299b247SNuri Amari    spare2: int
169a299b247SNuri Amari
170a299b247SNuri Amari    scatterOffset: int
171a299b247SNuri Amari
172a299b247SNuri Amari    teamOffset: int
173a299b247SNuri Amari
174a299b247SNuri Amari    spare3: int
175a299b247SNuri Amari    codeLimit64: int
176a299b247SNuri Amari
177a299b247SNuri Amari    execSegBase: int
178a299b247SNuri Amari    execSegLimit: int
179a299b247SNuri Amari    execSegFlags: int
180a299b247SNuri Amari
181a299b247SNuri Amari    @staticmethod
182a299b247SNuri Amari    def _format() -> str:
183a299b247SNuri Amari        return CodeDirectoryV20300._format() + "QQQ"
184a299b247SNuri Amari
185*b71edfaaSTobias Hieta
186a299b247SNuri Amariclass CodeDirectoryBlobIndex(typing.NamedTuple):
187a299b247SNuri Amari    type_: int
188a299b247SNuri Amari    offset: int
189a299b247SNuri Amari
190a299b247SNuri Amari    @staticmethod
191*b71edfaaSTobias Hieta    def make(buf: memoryview) -> "CodeDirectoryBlobIndex":
192*b71edfaaSTobias Hieta        return CodeDirectoryBlobIndex._make(
193*b71edfaaSTobias Hieta            struct.unpack_from(CodeDirectoryBlobIndex.__format(), buf, 0)
194*b71edfaaSTobias Hieta        )
195a299b247SNuri Amari
196a299b247SNuri Amari    @staticmethod
197a299b247SNuri Amari    def bytesize() -> int:
198a299b247SNuri Amari        return struct.calcsize(CodeDirectoryBlobIndex.__format())
199a299b247SNuri Amari
200a299b247SNuri Amari    @staticmethod
201a299b247SNuri Amari    def __format() -> str:
202a299b247SNuri Amari        return ">II"
203a299b247SNuri Amari
204*b71edfaaSTobias Hieta
205a299b247SNuri Amariclass CodeDirectorySuperBlob(typing.NamedTuple):
206a299b247SNuri Amari    magic: int
207a299b247SNuri Amari    length: int
208a299b247SNuri Amari    count: int
209a299b247SNuri Amari    blob_indices: typing.List[CodeDirectoryBlobIndex]
210a299b247SNuri Amari
211a299b247SNuri Amari    @staticmethod
212*b71edfaaSTobias Hieta    def make(buf: memoryview) -> "CodeDirectorySuperBlob":
213a299b247SNuri Amari        super_blob_layout = ">III"
214a299b247SNuri Amari        super_blob = struct.unpack_from(super_blob_layout, buf, 0)
215a299b247SNuri Amari
216a299b247SNuri Amari        offset = struct.calcsize(super_blob_layout)
217a299b247SNuri Amari        blob_indices = []
218a299b247SNuri Amari        for idx in range(super_blob[2]):
219a299b247SNuri Amari            blob_indices.append(CodeDirectoryBlobIndex.make(buf[offset:]))
220a299b247SNuri Amari            offset += CodeDirectoryBlobIndex.bytesize()
221a299b247SNuri Amari
222a299b247SNuri Amari        return CodeDirectorySuperBlob(*super_blob, blob_indices)
223a299b247SNuri Amari
224*b71edfaaSTobias Hieta
225a299b247SNuri Amaridef unpack_null_terminated_string(buf: memoryview) -> str:
226a299b247SNuri Amari    b = bytes(itertools.takewhile(lambda b: b != 0, buf))
227a299b247SNuri Amari    return b.decode()
228a299b247SNuri Amari
229*b71edfaaSTobias Hieta
230a299b247SNuri Amaridef main():
231a299b247SNuri Amari    parser = argparse.ArgumentParser()
232*b71edfaaSTobias Hieta    parser.add_argument(
233*b71edfaaSTobias Hieta        "binary", type=argparse.FileType("rb"), help="The file to analyze"
234*b71edfaaSTobias Hieta    )
235*b71edfaaSTobias Hieta    parser.add_argument(
236*b71edfaaSTobias Hieta        "offset", type=int, help="Offset to start of Code Directory data"
237*b71edfaaSTobias Hieta    )
238*b71edfaaSTobias Hieta    parser.add_argument("size", type=int, help="Size of Code Directory data")
239*b71edfaaSTobias Hieta    parser.add_argument(
240*b71edfaaSTobias Hieta        "code_offset", type=int, help="Offset to start of code pages to hash"
241*b71edfaaSTobias Hieta    )
242*b71edfaaSTobias Hieta    parser.add_argument("code_size", type=int, help="Size of the code pages to hash")
243a299b247SNuri Amari
244a299b247SNuri Amari    args = parser.parse_args()
245a299b247SNuri Amari
246a299b247SNuri Amari    args.binary.seek(args.offset)
247a299b247SNuri Amari    super_blob_bytes = args.binary.read(args.size)
248a299b247SNuri Amari    super_blob_mem = memoryview(super_blob_bytes)
249a299b247SNuri Amari
250a299b247SNuri Amari    super_blob = CodeDirectorySuperBlob.make(super_blob_mem)
251a299b247SNuri Amari    print(super_blob)
252a299b247SNuri Amari
253a299b247SNuri Amari    for blob_index in super_blob.blob_indices:
254a299b247SNuri Amari        code_directory_offset = blob_index.offset
255a299b247SNuri Amari        code_directory = CodeDirectory.make(super_blob_mem[code_directory_offset:])
256a299b247SNuri Amari        print(code_directory)
257a299b247SNuri Amari
258a299b247SNuri Amari        ident_offset = code_directory_offset + code_directory.identOffset
259*b71edfaaSTobias Hieta        print(
260*b71edfaaSTobias Hieta            "Code Directory ID: "
261*b71edfaaSTobias Hieta            + unpack_null_terminated_string(super_blob_mem[ident_offset:])
262*b71edfaaSTobias Hieta        )
263a299b247SNuri Amari
264a299b247SNuri Amari        code_offset = args.code_offset
265a299b247SNuri Amari        code_end = code_offset + args.code_size
266a299b247SNuri Amari        page_size = 1 << code_directory.pageSize
267a299b247SNuri Amari        args.binary.seek(code_offset)
268a299b247SNuri Amari
269a299b247SNuri Amari        hashes_offset = code_directory_offset + code_directory.hashOffset
270a299b247SNuri Amari        for idx in range(code_directory.nCodeSlots):
271*b71edfaaSTobias Hieta            hash_bytes = bytes(
272*b71edfaaSTobias Hieta                super_blob_mem[hashes_offset : hashes_offset + code_directory.hashSize]
273*b71edfaaSTobias Hieta            )
274a299b247SNuri Amari            hashes_offset += code_directory.hashSize
275a299b247SNuri Amari
276a299b247SNuri Amari            hasher = hashlib.sha256()
277a299b247SNuri Amari            read_size = min(page_size, code_end - code_offset)
278a299b247SNuri Amari            hasher.update(args.binary.read(read_size))
279a299b247SNuri Amari            calculated_hash_bytes = hasher.digest()
280a299b247SNuri Amari            code_offset += read_size
281a299b247SNuri Amari
282a299b247SNuri Amari            print("%s <> %s" % (hash_bytes.hex(), calculated_hash_bytes.hex()))
283a299b247SNuri Amari
284a299b247SNuri Amari            if hash_bytes != calculated_hash_bytes:
285a299b247SNuri Amari                sys.exit(-1)
286a299b247SNuri Amari
287a299b247SNuri Amari
288*b71edfaaSTobias Hietaif __name__ == "__main__":
289a299b247SNuri Amari    main()
290