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()