macho/macho-go/cloud-shield.py
2023-05-31 16:17:03 +07:00

156 lines
5.0 KiB
Python

import os
import shutil
import plistlib
import subprocess
import tempfile
import stat
joinp = os.path.join
wrapper_folder = os.path.expanduser("~/bcell/ios-wrapper-stable/bin")
sdk_root_folder = os.path.expanduser("~/bcell/ios-sdk")
class Worker:
@property
def __backup_folder(self):
# return tempfile.TemporaryDirectory(prefix="ios-wrapper")
return "/tmp/ios-wrapper-backup"
@property
def ios_wrapper(self):
return joinp(wrapper_folder, "ios-wrapper")
@property
def bcell_framework(self):
if self.encryption_required:
return joinp(sdk_root_folder, "release", "bazel-bin",
"bcell", "bcell.framework_archive-root", "bcell.framework/")
else:
return joinp(sdk_root_folder, "bazel-bin",
"bcell", "bcell.framework_archive-root", "bcell.framework/")
@property
def encryptor(self):
return joinp(sdk_root_folder, "release", "generated", "encryptor/encryptor.py")
@property
def binary_modified(self):
return joinp(self.__backup_folder, self.binary_name + "_modified")
@property
def bcell_raw(self):
return joinp(self.__backup_folder, "bcell_raw.dat")
@property
def bcell_unencrypted(self):
return joinp(self.__backup_folder, "bcell_unencrypted.dat")
@property
def bcell_encrypted(self):
return joinp(self.__backup_folder, "bcell_encrypted.dat")
def __load_binary(self):
self.binary = self.infile
self.binary_name = os.path.basename(self.infile)
def __init__(self, **kwargs):
for key in kwargs:
setattr(self, key, kwargs[key])
# prepare backup folder
shutil.rmtree(self.__backup_folder, ignore_errors=True)
os.mkdir(self.__backup_folder)
# load file based on file type
self.__load_binary()
if self.binary_name is None:
raise Exception("Cannot determine binary name")
def wrap(self):
if self.encryption_required:
subprocess.run([self.ios_wrapper, "wrap",
"-c", self.config,
"-s", "-b", self.bcell_raw,
"-o", self.binary_modified,
self.binary])
else:
subprocess.run([self.ios_wrapper, "wrap",
"-c", self.config,
"-b", self.bcell_raw,
"-o", self.binary_modified,
self.binary])
return
print("ENCRYPT BCELL FILE")
subprocess.run(["python3", self.encryptor,
"--infile", self.bcell_unencrypted,
"--outfile", self.bcell_encrypted])
def finish(self):
bcell_out = os.path.dirname(self.binary)
os.makedirs(joinp(bcell_out, "Frameworks"), exist_ok=True)
bcell_file = (self.bcell_raw, self.bcell_encrypted)[self.encryption_required]
shutil.copy(bcell_file, joinp(bcell_out, "bcell.dat"))
shutil.copy(self.binary_modified, joinp(bcell_out, self.binary_name))
shutil.rmtree(joinp(bcell_out, "Frameworks", "bcell.framework"), ignore_errors=True)
shutil.copytree(self.bcell_framework, joinp(bcell_out, "Frameworks", "bcell.framework"))
st = os.stat(joinp(bcell_out, self.binary_name))
os.chmod(joinp(bcell_out, self.binary_name), st.st_mode | stat.S_IEXEC)
self.info(joinp(bcell_out, self.binary_name))
def cleanup(self):
shutil.rmtree(self.__backup_folder, ignore_errors=True)
def info(self, file):
subprocess.run([self.ios_wrapper, "info", file])
def check_files(file_path):
if not os.path.isfile(file_path):
raise Exception("The file %s does not exist" % file_path)
return
def is_encryption_required():
return os.environ.get("REQUIRE_ENCRYPTION", None) is not None
if __name__ == "__main__":
import argparse
description = """
A test tool made for use with Xcode, paths to wrapper and bcell.framework
are hard-coded in this script, modify them if needed.
Pass in Xcode Build Phase a Run script:
```
python3 /path/to/cloud-shield.py \
-c ${PROJECT_DIR}/bcell_config.json \
-i ${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}
```
"""
parser = argparse.ArgumentParser(description=description,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("-i", "--infile", metavar="input-file",
required=True, help="Input binary built by Xcode Run script")
parser.add_argument("-c", "--config", metavar="config-file",
required=True, help="Config JSON file")
options = parser.parse_args()
check_files(options.infile)
check_files(options.config)
encryption_required = is_encryption_required()
worker = Worker(encryption_required=encryption_required,
infile=options.infile,
config=options.config)
worker.wrap()
worker.finish()
worker.cleanup()