macho/macho-go/tools/encryption-status.py
2023-05-31 16:17:03 +07:00

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)