-
Notifications
You must be signed in to change notification settings - Fork 5
/
MachODylibLoadCommandsFinder.py
100 lines (86 loc) · 3.75 KB
/
MachODylibLoadCommandsFinder.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
#!/usr/bin/env python3
import os
import lief
class MachODylibLoadCommandsFinder:
'''
Recursively crawl the system and parse Mach-O files to find DYLIB related load commands.
1. Check if the file is a Mach-O.
2. List all Load Commands.
3. Check if any DYLIB-related LC exists.
LC_LOAD_DYLIB
LC_ID_DYLIB
LC_PREBOUND_DYLIB
LC_LOAD_WEAK_DYLIB
LC_REEXPORT_DYLIB
LC_LAZY_LOAD_DYLIB
LC_LOAD_UPWARD_DYLIB
LC_RPATH
4. Print the total Mach-O files analyzed and how many DYLIB-related LCs existed.
'''
def __init__(self):
self.total_files_analyzed = 0
self.binary_dylibs = {}
self.dylib_counts = {
"LC_LOAD_DYLIB" : 0,
"LC_ID_DYLIB": 0,
"LC_PREBOUND_DYLIB": 0,
"LC_LOAD_WEAK_DYLIB": 0,
"LC_REEXPORT_DYLIB": 0,
"LC_LAZY_LOAD_DYLIB": 0,
"LC_LOAD_UPWARD_DYLIB": 0,
"LC_RPATH": 0,
}
def parseDirectory(self, directory_path):
'''Recursively check if the path is a file. If it is, use checkIfMacho method.'''
for root, dirs, files in os.walk(directory_path):
for file in files:
file_path = os.path.join(root, file)
if os.path.isfile(file_path):
self.checkIfMacho(file_path)
def checkIfMacho(self, file_path):
binaries = lief.MachO.parse(file_path)
if binaries:
self.parseFatBinary(binaries, file_path)
def parseFatBinary(self, binaries, file_path):
for binary in binaries:
if binary.header.cpu_type == lief.MachO.CPU_TYPES.ARM64:
self.total_files_analyzed += 1
self.checkDylibLoadCommands(binary, file_path)
def checkDylibLoadCommands(self, binary, file_path):
dylib_related_lcs = {
lief.MachO.LOAD_COMMAND_TYPES.LOAD_DYLIB: "LC_LOAD_DYLIB",
lief.MachO.LOAD_COMMAND_TYPES.ID_DYLIB: "LC_ID_DYLIB",
lief.MachO.LOAD_COMMAND_TYPES.PREBOUND_DYLIB: "LC_PREBOUND_DYLIB",
lief.MachO.LOAD_COMMAND_TYPES.LOAD_WEAK_DYLIB: "LC_LOAD_WEAK_DYLIB",
lief.MachO.LOAD_COMMAND_TYPES.REEXPORT_DYLIB: "LC_REEXPORT_DYLIB",
lief.MachO.LOAD_COMMAND_TYPES.LAZY_LOAD_DYLIB: "LC_LAZY_LOAD_DYLIB",
lief.MachO.LOAD_COMMAND_TYPES.LOAD_UPWARD_DYLIB: "LC_LOAD_UPWARD_DYLIB",
lief.MachO.LOAD_COMMAND_TYPES.RPATH: "LC_RPATH",
}
binary_dylibs_set = set()
for cmd in binary.commands:
if cmd.command in dylib_related_lcs:
lc_name = dylib_related_lcs[cmd.command]
self.dylib_counts[lc_name] += 1
binary_dylibs_set.add(lc_name)
self.binary_dylibs[file_path] = binary_dylibs_set
def print_results(self):
print(f"Total Mach-O files analyzed: {self.total_files_analyzed}")
print("DYLIB-related LC counts:")
for lc, count in self.dylib_counts.items():
print(f"{lc}: {count}")
print("\nBinary Dylibs:")
for binary, dylibs in self.binary_dylibs.items():
print(f"{binary}: {dylibs}")
def save_results(self):
with open("MachODylibLoadCommandsFinder_results.txt", "a") as f:
f.write(f"Total Mach-O files analyzed: {self.total_files_analyzed}\n")
f.write("DYLIB-related LC counts:\n")
for lc, count in self.dylib_counts.items():
f.write(f"{lc}: {count}\n")
for binary, dylibs in self.binary_dylibs.items():
f.write(f"{binary}: {', '.join(dylibs)}\n")
macho_checker = MachODylibLoadCommandsFinder()
macho_checker.parseDirectory("/")
macho_checker.print_results()
macho_checker.save_results()