Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 20 additions & 54 deletions examples/example_ios_ali_vmp_sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from chomper import Chomper
from chomper.const import ARCH_ARM64, OS_IOS
from chomper.objc import ObjC
from chomper.utils import pyobj2nsobj

base_path = os.path.abspath(os.path.dirname(__file__))

Expand All @@ -28,53 +29,24 @@ def decorator(uc, address, size, user_data):
return decorator


def hook_ns_bundle(emu, objc):
bundle_identifier = objc.msg_send("NSString", "stringWithUTF8String:", "com.ceair.b2m")
def hook_ns_bundle(emu):
executable_path = f"/var/containers/Bundle/Application/{uuid.uuid4()}/com.ceair.b2m/ceair_iOS_branch"

executable_path = objc.msg_send(
"NSString",
"stringWithUTF8String:",
f"/var/containers/Bundle/Application/{uuid.uuid4()}/com.ceair.b2m/ceair_iOS_branch",
)

bundle_info_directory = objc.msg_send(
"NSMutableDictionary",
"dictionaryWithObject:forKey:",
objc.msg_send("NSString", "stringWithUTF8String:", "9.4.7"),
objc.msg_send("NSString", "stringWithUTF8String:", "CFBundleShortVersionString"),
)

objc.msg_send(
bundle_info_directory,
"addObject:forKey:",
executable_path,
objc.msg_send("NSString", "stringWithUTF8String:", "CFBundleExecutable"),
)
bundle_info = {
"CFBundleShortVersionString": "9.4.7",
"CFBundleExecutable": executable_path,
}

emu.add_interceptor("-[NSBundle initWithPath:]", hook_skip)
emu.add_interceptor("-[NSBundle bundleIdentifier]", hook_retval(bundle_identifier))
emu.add_interceptor("-[NSBundle executablePath]", hook_retval(executable_path))
emu.add_interceptor("-[NSBundle infoDictionary]", hook_retval(bundle_info_directory))


def hook_ns_locale(emu, objc):
preferred_languages = objc.msg_send(
"NSArray",
"arrayWithObject:",
objc.msg_send("NSString", "stringWithUTF8String:", "zh-cn")
)

emu.add_interceptor("+[NSLocale preferredLanguages]", hook_retval(preferred_languages))
emu.add_interceptor("-[NSBundle bundleIdentifier]", hook_retval(pyobj2nsobj(emu, "com.ceair.b2m")))
emu.add_interceptor("-[NSBundle executablePath]", hook_retval(pyobj2nsobj(emu, executable_path)))
emu.add_interceptor("-[NSBundle infoDictionary]", hook_retval(pyobj2nsobj(emu, bundle_info)))


def hook_ui_device(emu, objc):
system_version = objc.msg_send("NSString", "stringWithUTF8String:", "14.4.0")
device_name = objc.msg_send("NSString", "stringWithUTF8String:", "iPhone")
device_model = objc.msg_send("NSString", "stringWithUTF8String:", "iPhone13,1")

emu.add_interceptor("-[UIDevice systemVersion]", hook_retval(system_version))
emu.add_interceptor("-[UIDevice name]", hook_retval(device_name))
emu.add_interceptor("-[UIDevice model]", hook_retval(device_model))
def hook_ui_device(emu):
emu.add_interceptor("-[UIDevice systemVersion]", hook_retval(pyobj2nsobj(emu, "14.4.0")))
emu.add_interceptor("-[UIDevice name]", hook_retval(pyobj2nsobj(emu, "iPhone")))
emu.add_interceptor("-[UIDevice model]", hook_retval(pyobj2nsobj(emu, "iPhone13,1")))


def main():
Expand All @@ -88,9 +60,8 @@ def main():

objc = ObjC(emu)

hook_ns_bundle(emu, objc)
hook_ns_locale(emu, objc)
hook_ui_device(emu, objc)
hook_ns_bundle(emu)
hook_ui_device(emu)

# Skip a file operation
emu.add_interceptor("_fopen", hook_retval(0))
Expand All @@ -99,16 +70,11 @@ def main():

ali_tiger_tally_instance = objc.msg_send("AliTigerTally", "sharedInstance")

app_key = objc.msg_send(
"NSString",
"stringWithUTF8String:",
"xPEj7uv0KuziQnXUyPIBNUjnDvvHuW09VOYFuLYBcY-jV6fgqmfy5B1y75_iSuRM5U2zNq7MRoR9N1F-UthTEgv-QBWk68gr95BrAySzWuDzt08FrkeBZWQCGyZ0iAybalYLOJEF7nkKBtmDGLewcw==",
)

objc.msg_send(ali_tiger_tally_instance, "initialize:", app_key)
app_key = "xPEj7uv0KuziQnXUyPIBNUjnDvvHuW09VOYFuLYBcY-jV6fgqmfy5B1y75_iSuRM5U2zNq7MRoR9N1F-UthTEgv-QBWk68gr95BrAySzWuDzt08FrkeBZWQCGyZ0iAybalYLOJEF7nkKBtmDGLewcw=="
objc.msg_send(ali_tiger_tally_instance, "initialize:", pyobj2nsobj(emu, app_key))

encrypt_str = objc.msg_send("NSString", "stringWithUTF8String:", '{"biClassId":["2","3","4"]}')
encrypt_bytes = objc.msg_send(encrypt_str, "dataUsingEncoding:", 1)
encrypt_str = '{"biClassId":["2","3","4"]}'
encrypt_bytes = objc.msg_send(pyobj2nsobj(emu, encrypt_str), "dataUsingEncoding:", 1)

vmp_sign = objc.msg_send(ali_tiger_tally_instance, "vmpSign:", encrypt_bytes)
logger.info("vmp sign: %s", emu.read_string(objc.msg_send(vmp_sign, "cStringUsingEncoding:", 4)))
Expand Down
33 changes: 10 additions & 23 deletions examples/example_ios_bangbang.py

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions examples/example_ios_ijm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from chomper import Chomper
from chomper.const import ARCH_ARM64, OS_IOS
from chomper.objc import ObjC
from chomper.utils import pyobj2nsobj

base_path = os.path.abspath(os.path.dirname(__file__))

Expand Down Expand Up @@ -42,16 +43,15 @@ def main():
emu.add_interceptor(czair.base + 0x1038F0004, hook_retval(1))

# Call encryption
encrypt_input = objc.msg_send("NSString", "stringWithUTF8String:", '{"biClassId":["2","3","4"]}')
encrypt_str = '{"biClassId":["2","3","4"]}'
encrypt_result = objc.msg_send("JMBox125", "JMBox167:JMBox501:", pyobj2nsobj(emu, encrypt_str), 1)

encrypt_result = objc.msg_send("JMBox125", "JMBox167:JMBox501:", encrypt_input, 1)
logger.info("encrypt_result: %s", emu.read_string(objc.msg_send(encrypt_result, "cStringUsingEncoding:", 4)))

# Call decryption
decrypt_data = "XKQYFMCP9Eb0IUzrQ9KaRRvTeFcYYyLcInrS/IWp6be1+VZa14GanCrzeb3DR45HW+XH0xiZLA5WUjUcXnlpM+CC6EtauUDUxCLap3QPWRyewLUosCB/ESHE7341DQca6lx5KFcP0XCkBpGlEKpACR5v7TwNBxc62auNBDvmEY422LTAUEEBrC8FDE+Y4DS2IJTLN6h9f7hdmQ4zUnY4cwyZXwgdIoH+bVuNy6TSw1JjQaFF/fLLHVZOQovrMcjtTpMZGr8xOSoW/+msiZzKwET3"
decrypt_input = objc.msg_send("NSString", "stringWithUTF8String:", decrypt_data)
decrypt_str = "XKQYFMCP9Eb0IUzrQ9KaRRvTeFcYYyLcInrS/IWp6be1+VZa14GanCrzeb3DR45HW+XH0xiZLA5WUjUcXnlpM+CC6EtauUDUxCLap3QPWRyewLUosCB/ESHE7341DQca6lx5KFcP0XCkBpGlEKpACR5v7TwNBxc62auNBDvmEY422LTAUEEBrC8FDE+Y4DS2IJTLN6h9f7hdmQ4zUnY4cwyZXwgdIoH+bVuNy6TSw1JjQaFF/fLLHVZOQovrMcjtTpMZGr8xOSoW/+msiZzKwET3"
decrypt_result = objc.msg_send("JMBox125", "JMBox167:JMBox501:", pyobj2nsobj(emu, decrypt_str), 1)

decrypt_result = objc.msg_send("JMBox125", "JMBox167:JMBox501:", decrypt_input, 1)
logger.info("decrypt_result: %s", emu.read_string(objc.msg_send(decrypt_result, "cStringUsingEncoding:", 4)))


Expand Down
18 changes: 10 additions & 8 deletions src/chomper/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,10 @@ def _setup_emulator(self, enable_vfp: bool = True):
if self.arch == arm_arch and enable_vfp:
self._enable_vfp()

def _start_emulate(self, address: int, *args: int):
def _start_emulate(self, address: int, *args: int) -> int:
"""Start emulate at the specified address."""
context = self.uc.context_save()
stop_addr = self.create_buffer(8)
# stop_addr = 0

for index, value in enumerate(args):
self.set_arg(index, value)
Expand All @@ -208,12 +208,18 @@ def _start_emulate(self, address: int, *args: int):
self.logger.info(f"Start emulate at {self.debug_symbol(address)}")
self.uc.emu_start(address, stop_addr)

return self.get_retval()

except UcError as e:
self.crash("Unknown reason", from_exc=e)

finally:
self.uc.context_restore(context)
self.free(stop_addr)

# Pass type hints
return 0

def find_module(self, name_or_addr: Union[str, int]) -> Optional[Module]:
"""Find module by name or address."""
for module in self.modules:
Expand Down Expand Up @@ -739,12 +745,8 @@ def call_symbol(self, symbol_name: str, *args: int) -> int:
symbol = self.find_symbol(symbol_name)
address = symbol.address

self._start_emulate(address, *args)

return self.get_retval()
return self._start_emulate(address, *args)

def call_address(self, address: int, *args: int) -> int:
"""Call function at the address."""
self._start_emulate(address, *args)

return self.get_retval()
return self._start_emulate(address, *args)
45 changes: 19 additions & 26 deletions src/chomper/objc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,25 @@ class ObjC:
def __init__(self, emu):
self.emu = emu

self._class_cache = {}
self._sel_cache = {}

def get_class(self, class_name: str) -> int:
"""Get class by name."""
if class_name in self._class_cache:
return self._class_cache[class_name]

class_name_ptr = self.emu.create_string(class_name)
name_ptr = self.emu.create_string(class_name)

cls = self.emu.call_symbol("_objc_getClass", class_name_ptr)
self._class_cache[class_name] = cls
try:
return self.emu.call_symbol("_objc_getClass", name_ptr)

return cls
finally:
self.emu.free(name_ptr)

def get_sel(self, sel_name: str) -> int:
"""Get selector by name."""
if sel_name in self._sel_cache:
return self._sel_cache[sel_name]
name_ptr = self.emu.create_string(sel_name)

sel_name_ptr = self.emu.create_string(sel_name)

sel = self.emu.call_symbol("_sel_registerName", sel_name_ptr)
self._sel_cache[sel_name] = sel
try:
return self.emu.call_symbol("_sel_registerName", name_ptr)

return sel
finally:
self.emu.free(name_ptr)

def msg_send(self, receiver: Union[int, str], sel: Union[int, str], *args) -> int:
"""Send message to Objective-C runtime.
Expand All @@ -47,25 +40,25 @@ def msg_send(self, receiver: Union[int, str], sel: Union[int, str], *args) -> in
receiver = self.get_class(receiver) if isinstance(receiver, str) else receiver
sel = self.get_sel(sel) if isinstance(sel, str) else sel

c_strs = []
args_ = []
str_ptrs = []
new_args = []

for arg in args:
if isinstance(arg, str):
c_str = self.emu.create_string(arg)
str_ptr = self.emu.create_string(arg)

c_strs.append(c_str)
args_.append(c_str)
str_ptrs.append(str_ptr)
new_args.append(str_ptr)

else:
args_.append(arg)
new_args.append(arg)

try:
return self.emu.call_symbol("_objc_msgSend", receiver, sel, *args_)
return self.emu.call_symbol("_objc_msgSend", receiver, sel, *new_args)

finally:
for c_str in c_strs:
self.emu.free(c_str)
for str_ptr in str_ptrs:
self.emu.free(str_ptr)

def release(self, obj: int):
"""Release object."""
Expand Down
6 changes: 4 additions & 2 deletions src/chomper/os/ios/fixup.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,11 @@ def fixup_cfstring_section(self, binary: lief.MachO.Binary, module_base: int):
offset = 0

while offset < section.size:
address = module_base + start + offset + 0x10
self.relocate_pointer(module_base, address)
self.add_refs_relocation(start + offset)

str_ptr = module_base + start + offset + 0x10
self.relocate_pointer(module_base, str_ptr)

offset += step

def fixup_cf_uni_char_string(self, binary: lief.MachO.Binary):
Expand Down
86 changes: 86 additions & 0 deletions src/chomper/os/ios/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

from unicorn.unicorn import UC_HOOK_CODE_TYPE

from chomper.utils import pyobj2nsobj

hooks: Dict[str, UC_HOOK_CODE_TYPE] = {}


Expand Down Expand Up @@ -379,11 +381,95 @@ def hook_dispatch_async(uc, address, size, user_data):
return 0


@register_hook("_uloc_getLanguage")
def hook_uloc_get_language(uc, address, size, user_data):
return 0


@register_hook("_uloc_getScript")
def hook_uloc_get_script(uc, address, size, user_data):
return 0


@register_hook("_uloc_getCountry")
def hook_uloc_get_country(uc, address, size, user_data):
return 0


@register_hook("_uloc_getVariant")
def hook_uloc_get_variant(uc, address, size, user_data):
return 0


@register_hook("_uloc_openKeywords")
def hook_uloc_open_keywords(uc, address, size, user_data):
return 0


@register_hook("_uloc_getDisplayName")
def hook_uloc_get_display_name(uc, address, size, user_data):
return 0


@register_hook("_uloc_getDisplayLanguage")
def hook_uloc_get_display_language(uc, address, size, user_data):
return 0


@register_hook("_uenum_next")
def hook_uenum_next(uc, address, size, user_data):
return 0


@register_hook("_uenum_close")
def hook_uenum_close(uc, address, size, user_data):
return 0


@register_hook("_os_log_type_enabled")
def hook_os_log_type_enabled(uc, address, size, user_data):
return 0


@register_hook("__CFPreferencesCopyAppValueWithContainerAndConfiguration")
def hook_cf_preferences_copy_app_value_with_container_and_configuration(
uc, address, size, user_data
):
emu = user_data["emu"]

str_ptr = emu.read_pointer(emu.get_arg(0) + 0x10)
key = emu.read_string(str_ptr)

if key in emu.os.preferences:
return pyobj2nsobj(emu, emu.os.preferences[key])

return 0


@register_hook("__CFBundleCreateInfoDictFromMainExecutable")
def hook_cf_bundle_create_info_dict_from_main_executable(uc, address, size, user_data):
return 0


@register_hook("___CFXPreferencesCopyCurrentApplicationStateWithDeadlockAvoidance")
def hook_cf_x_preferences_copy_current_application_state_with_deadlock_avoidance(
uc, address, size, user_data
):
emu = user_data["emu"]

return pyobj2nsobj(emu, emu.os.preferences)


@register_hook("_CFNotificationCenterGetLocalCenter")
def hook_cf_notification_center_get_local_center(uc, address, size, user_data):
return 0


@register_hook("__CFPrefsClientLog")
def hook_cf_prefs_client_log(uc, address, size, user_data):
return 0


@register_hook("_NSLog")
def hook_ns_log(uc, address, size, user_data):
return 0
Loading