113 lines
3.0 KiB
Python
113 lines
3.0 KiB
Python
import os
|
|
import subprocess
|
|
import plistlib
|
|
|
|
import sys
|
|
|
|
import io
|
|
|
|
|
|
FAT_MAGIC = b'\xbe\xba\xfe\xca'
|
|
FAT_CIGAM = b'\xca\xfe\xba\xbe'
|
|
MH_MAGIC = b'\xce\xfa\xed\xfe'
|
|
MH_CIGAM = b'\xfe\xed\xfa\xce'
|
|
MH_MAGIC_64 = b'\xcf\xfa\xed\xfe'
|
|
MH_CIGAM_64 = b'\xfe\xed\xfa\xcd'
|
|
|
|
LC_ENCRYPTION_INFO = 0x21
|
|
LC_ENCRYPTION_INFO_64 = 0x2C
|
|
|
|
class Macho:
|
|
class LoadCommand():
|
|
def __init__(self, cmd, buf):
|
|
self.cmd = cmd
|
|
self.buf = buf
|
|
|
|
class EncryptionInfo():
|
|
def __init__(self, load_command):
|
|
cryptid = load_command.buf[4*4:4*4+4]
|
|
self.encrypted = cryptid != b'\x00\x00\x00\x00'
|
|
|
|
def __init__(self, buf):
|
|
self.buf = buf
|
|
self.buf.seek(0)
|
|
self.magic = self.buf.read(4)
|
|
|
|
if self.magic in [MH_MAGIC, MH_MAGIC_64]:
|
|
self.endian = "little"
|
|
else:
|
|
self.endian = "big"
|
|
|
|
if self.magic in [MH_MAGIC, MH_CIGAM]:
|
|
self.bittype = "32"
|
|
else:
|
|
self.bittype = "64"
|
|
|
|
def get_encryption_info(self):
|
|
for load_command in self.__traverse_load_commands():
|
|
if load_command.cmd in [LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64]:
|
|
einfo = Macho.EncryptionInfo(load_command)
|
|
return einfo.encrypted
|
|
|
|
def __traverse_load_commands(self):
|
|
ncmds = self.ncmds()
|
|
self.buf.seek(self.load_commands_start())
|
|
|
|
for i in range(ncmds):
|
|
old = self.buf.tell()
|
|
b = self.buf.read(8)
|
|
self.buf.seek(old)
|
|
cmd = int.from_bytes(b[:4], self.endian)
|
|
cmdsize = int.from_bytes(b[4:], self.endian)
|
|
yield Macho.LoadCommand(cmd, self.buf.read(cmdsize))
|
|
|
|
def ncmds(self):
|
|
self.buf.seek(16)
|
|
ncmds = self.buf.read(4)
|
|
return int.from_bytes(ncmds, self.endian)
|
|
|
|
def load_commands_start(self):
|
|
if self.bittype == "32":
|
|
return 7 * 4
|
|
else:
|
|
return 8 * 4
|
|
|
|
# return a list of Macho
|
|
def load_binary(path):
|
|
f = open(path, 'rb')
|
|
magic = f.read(4)
|
|
if magic in [FAT_CIGAM, FAT_MAGIC]:
|
|
# do fat stuff
|
|
print("fat not supported yet")
|
|
pass
|
|
else:
|
|
f.seek(0)
|
|
return [Macho(f)]
|
|
return []
|
|
|
|
def encryption_status(path):
|
|
machos = load_binary(path)
|
|
for macho in machos:
|
|
print(f"{path}: {macho.magic} | encryption:{macho.get_encryption_info()}")
|
|
|
|
def get_app(app_folder):
|
|
plist = os.path.join(app_folder, "Info.plist")
|
|
plist = plistlib.load(open(plist, "rb"))
|
|
binary_name = plist.get("CFBundleExecutable", None)
|
|
return os.path.join(app_folder, binary_name)
|
|
|
|
if __name__ == "__main__":
|
|
|
|
app_folder = sys.argv[1]
|
|
|
|
pjoin = os.path.join
|
|
framework_folder = pjoin(app_folder, "Frameworks")
|
|
plugin_folder = pjoin(app_folder, "Plugins")
|
|
|
|
app = get_app(app_folder)
|
|
encryption_status(app)
|
|
|
|
for framework in os.listdir(framework_folder):
|
|
app = get_app(pjoin(framework_folder, framework))
|
|
encryption_status(app)
|