sledgeh4w / chomper

A lightweight emulation framework for emulating encryption of iOS executables and libraries.
MIT License
278 stars 69 forks source link

Can't create NSURLRequest object. #68

Closed Yiiff closed 7 months ago

Yiiff commented 7 months ago

Crash with creating NSURLRequest object.

The reference code is:

NSMutableURLRequest *reqM = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.google.com"]];

Code1:

def create_ns_url(emu, s):
    cls = objc_get_class(emu, "NSURL")
    alloc_sel = objc_sel_register_name(emu, "alloc")
    init_sel = objc_sel_register_name(emu, "initWithString:")
    space = emu.call_symbol("_objc_msgSend", cls, alloc_sel)
    return emu.call_symbol("_objc_msgSend", space, init_sel, create_ns_string(emu, s))

def create_ns_mutable_url_request(emu, s):
    ns_url_obj = create_ns_url(emu, s)
    cls = objc_get_class(emu, class_name="NSMutableURLRequest")
    sel = objc_sel_register_name(emu, "requestWithURL:")
    req_obj = emu.call_symbol("_objc_msgSend", cls, sel, ns_url_obj)
    return req_obj

Code2:

def create_ns_url(emu, s):
    cls = objc_get_class(emu, "NSURL")
    alloc_sel = objc_sel_register_name(emu, "alloc")
    init_sel = objc_sel_register_name(emu, "initWithString:")
    space = emu.call_symbol("_objc_msgSend", cls, alloc_sel)
    return emu.call_symbol("_objc_msgSend", space, init_sel, create_ns_string(emu, s))

def create_ns_mutable_url_request(emu, s):
    ns_url_obj = create_ns_url(emu, s)
    cls = objc_get_class(emu, class_name="NSMutableURLRequest")
    alloc_sel = objc_sel_register_name(emu, "alloc")
    init_sel = objc_sel_register_name(emu, "initWithString:")
    space = emu.call_symbol("_objc_msgSend", cls, alloc_sel)
    req_obj = emu.call_symbol("_objc_msgSend", space, init_sel, ns_url_obj)
    return req_obj

The crash happens on this line:

req_obj = emu.call_symbol("_objc_msgSend", cls, sel, ns_url_obj)
req_obj = emu.call_symbol("_objc_msgSend", space, init_sel, ns_url_obj)

Error Log:

2024-04-23 15:11:31,447 - __main__ - INFO: Start emulate at libobjc.A.dylib!0x1947ec460
2024-04-23 15:11:45,130 - __main__ - INFO: Registers: x0: 0x0000000000000000, x1: 0x00000000000772b8, x2: 0x0000000000000000, x3: 0x0000000000000001, x4: 0x000000005d3799ff [libsystem_c.dylib!0x18916e9ff], x5: 0x0000000000000000, x6: 0x0000000000000000, x7: 0x0000000000000036, x8: 0x000000000803c080, x9: 0x00000000000774f8, x10: 0x0000000000000000, x11: 0x000000024ed2f829 [libdyld.dylib!0x1800f3829], x12: 0x000000024ed12568 [libdyld.dylib!0x1800d6568], x13: 0x0000000000000000, x14: 0x000000000801000d, x15: 0x0000000000000000, x16: 0xfffffffffffffff6, x17: 0x0000000000000033, x18: 0x0000000000000000, x19: 0x0000000000000000, x20: 0x00000000000772b8, x21: 0x0000000000000000, x22: 0x000000005d3720a8 [libsystem_c.dylib!0x1891670a8], x23: 0x0000000000000001, x24: 0x0000000000000000, x25: 0x00000002e6d730f1 [libobjc.A.dylib!0x1cb75d0f1], x26: 0x00000009f1c28f90 [binary!0x10c128f90], x27: 0x00000002f500d000 [libobjc.A.dylib!0x1d99f7000], x28: 0x00000002f5f2b000 [libobjc.A.dylib!0x1da915000], x29: 0x00000000000772a0, x30: 0x000000002850e3f0 [libsystem_kernel.dylib!0x1ac3153f0]
2024-04-23 15:11:45,131 - __main__ - INFO: Trace stack: libsystem_kernel.dylib!0x1ac30b56c, libsystem_kernel.dylib!0x1ac3153ec, libsystem_kernel.dylib!0x1ac315598, libsystem_platform.dylib!0x1c8be956c, libdyld.dylib!0x1800f158c, libdyld.dylib!0x1800f1620, libdyld.dylib!0x1800f1994, libdyld.dylib!0x1800d1eb8, libdyld.dylib!0x1800d6570, libdyld.dylib!0x1800c9754, libsystem_c.dylib!0x1891670a4, libsystem_darwin.dylib!0x1c8ba9c68, libsystem_darwin.dylib!0x1c8ba4788, CFNetwork!0x180b174ac, libdispatch.dylib!0x1800a627c, libdispatch.dylib!0x180077cd4, CFNetwork!0x180b17688, libdispatch.dylib!0x1800a627c, libdispatch.dylib!0x180077cd4, CFNetwork!0x180c11360, libobjc.A.dylib!0x1947efb94, libobjc.A.dylib!0x19480f0b4, CFNetwork!0x180a4b75c, CFNetwork!0x180a4b658
Traceback (most recent call last):
  File "/Users/xxx/Documents/repos/chomper/venv/lib/python3.9/site-packages/chomper/core.py", line 200, in _start_emulate
    self.uc.emu_start(address, stop_addr)
  File "/Users/xxx/Documents/repos/chomper/venv/lib/python3.9/site-packages/unicorn/unicorn.py", line 550, in emu_start
    raise self._hook_exception
  File "/Users/xxx/Documents/repos/chomper/venv/lib/python3.9/site-packages/unicorn/unicorn.py", line 392, in wrapper
    return func(self, *args, **kwargs)
  File "/Users/xxx/Documents/repos/chomper/venv/lib/python3.9/site-packages/unicorn/unicorn.py", line 681, in _hook_intr_cb
    cb(self, intno, data)
  File "/Users/xxx/Documents/repos/chomper/venv/lib/python3.9/site-packages/chomper/core.py", line 417, in _interrupt_callback
    self._dispatch_syscall()
  File "/Users/xxx/Documents/repos/chomper/venv/lib/python3.9/site-packages/chomper/core.py", line 441, in _dispatch_syscall
    self.crash("Unhandled system call")
  File "/Users/xxx/Documents/repos/chomper/venv/lib/python3.9/site-packages/chomper/core.py", line 371, in crash
    raise EmulatorCrashedException(message)
chomper.exceptions.EmulatorCrashedException: Unhandled system call at libsystem_kernel.dylib!0x1ac30b56c
python-BaseException
sledgeh4w commented 7 months ago

What is your final target? It is possible to create an NSURLRequest object, but chomper currently does not support networking, so you cannot actually send a network request at this time.

Regarding this exception, it was raised by function _os_variant_allows_internal_security_policie and the function depend on a sysctl command kern.osvariant_status (chomper not handled this scene correctly). The simplest solution is to add hook to the function and make it return 1 direct. For the handling of kern.osvariant_status, I will fix it later.

emu.add_interceptor("_os_variant_allows_internal_security_policies", hook_retval(1))

In addition, there is an little error in your second case: initWithString: -> initWithURL: (This is also the part currently lacking in chomper, friendly error prompts).

Yiiff commented 7 months ago

What is your final target? It is possible to create an NSURLRequest object, but chomper currently does not support networking, so you cannot actually send a network request at this time.

Regarding this exception, it was raised by function _os_variant_allows_internal_security_policie and the function depend on a sysctl command kern.osvariant_status (chomper not handled this scene correctly). The simplest solution is to add hook to the function and make it return 1 direct. For the handling of kern.osvariant_status, I will fix it later.

emu.add_interceptor("_os_variant_allows_internal_security_policies", hook_retval(1))

In addition, there is an little error in your second case: initWithString: -> initWithURL: (This is also the part currently lacking in chomper, friendly error prompts).

My ultimate goal is not to complete the network request, but the function to be hooked has an input parameter of type NSURLRequest, so I need to construct this object to complete the function call.

The error you mentioned has been fixed. Thank you. It was indeed my oversight. The current error prompts is indeed not very convenient. It takes some time to analyze.


I took your advice, but I still can't create an NSURLRequest object

I suspect it's because one of the input parameters of NSURLRequest is NSURL, and I have a problem passing the NSURL object directly

Additionally, there is no problem creating NSDictionary, NSString, NSArray, NSURL and other classes.

sledgeh4w commented 7 months ago

Strange, I tested your code and there were no errors. Can you post your whole code?

Yiiff commented 7 months ago

Strange, I tested your code and there were no errors. Can you post your whole code?

Sure, this is my py file

import logging
import os

from chomper.core import Chomper
from chomper.const import ARCH_ARM64, OS_IOS
from chomper.os.ios.options import IosOptions

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

log_format = "%(asctime)s - %(name)s - %(levelname)s: %(message)s"
logging.basicConfig(
    format=log_format,
    level=logging.INFO,
)

logger = logging.getLogger(__name__)

def create_emulator():
    options = IosOptions(enable_objc=True, enable_ui_kit=True)
    emu = Chomper(
        arch=ARCH_ARM64,
        os_type=OS_IOS,
        logger=logger,
        rootfs_path=os.path.join(base_path, "ios/rootfs"),
        os_options=options,
    )
    return emu

def objc_get_class(emu, class_name):
    return emu.call_symbol("_objc_getClass", emu.create_string(class_name))

def objc_sel_register_name(emu, sel_name):
    return emu.call_symbol("_sel_registerName", emu.create_string(sel_name))

def create_ns_string(emu, s):
    ns_string_class = objc_get_class(emu, "NSString")
    string_with_utf8_string_sel = objc_sel_register_name(emu, "stringWithUTF8String:")
    obj = emu.call_symbol(
        "_objc_msgSend",
        ns_string_class,
        string_with_utf8_string_sel,
        emu.create_string(s),
    )
    return obj

def read_ns_string(emu, obj):
    c_string_using_encoding_sel = objc_sel_register_name(emu, "cStringUsingEncoding:")
    ptr = emu.call_symbol("_objc_msgSend", obj, c_string_using_encoding_sel, 4)
    return emu.read_string(ptr)

def create_ns_url(emu, s):
    cls = objc_get_class(emu, "NSURL")
    alloc_sel = objc_sel_register_name(emu, "alloc")
    init_sel = objc_sel_register_name(emu, "initWithString:")
    space = emu.call_symbol("_objc_msgSend", cls, alloc_sel)
    return emu.call_symbol("_objc_msgSend", space, init_sel, create_ns_string(emu, s))

def create_ns_mutable_url_request(emu, s):
    ns_url_obj = create_ns_url(emu, s)
    cls = objc_get_class(emu, class_name="NSURLRequest")
    sel = objc_sel_register_name(emu, "requestWithURL:")
    req_obj = emu.call_symbol("_objc_msgSend", cls, sel, ns_url_obj)

    emu.add_interceptor("_os_variant_allows_internal_security_policies", hook_retval(1))

    return req_obj

def hook_retval(retval):
    def decorator(uc, address, size, user_data):
        return retval
    return decorator

def hook_sec_item(emu):
    emu.add_interceptor("_SecItemCopyMatching", hook_retval(0))
    emu.add_interceptor("_SecItemUpdate", hook_retval(0))
    emu.add_interceptor("_CFRelease", hook_retval(0))

def main():
    emu = create_emulator()

    hook_sec_item(emu)

    emu.load_module(module_file=os.path.join(base_path, "ios/apps/com.xxx.xx/target"))
    logger.info("[Test] exec load")

    url_str = "https://www.test.com"
    url_obj = create_ns_mutable_url_request(emu, s=url_str)

    logger.info("[Test] finish.")

if __name__ == "__main__":
    logger.info("[Test] launch")
    main()
sledgeh4w commented 7 months ago

You should add the hook before create object, just like hook_sec_item.

Yiiff commented 7 months ago

You should add the hook before create object, just like hook_sec_item.

Thank you for your support. I will try it. I copied hook_sec_item from the demo. I don't really understand its function. So I kept it in the code.

But your description gave me a new question. If I don't hook, can't I use the calling class directly?

Yiiff commented 7 months ago

You should add the hook before create object, just like hook_sec_item.

I understand what you mean by hook, but I don't understand what to hook. For example, I need to actively call NSURLRequest and construct an object to pass parameters, so I need to hook NSURLRequest in advance?

Although it's a bit embarrassing because I haven't figured it out, can you provide your code that works? Then I want to study where I went wrong.

sledgeh4w commented 7 months ago

What I mean is:

emu.add_interceptor("_os_variant_allows_internal_security_policies", hook_retval(1))
create_ns_mutable_url_request(emu, s=url_str)
Yiiff commented 7 months ago

What I mean is:

emu.add_interceptor("_os_variant_allows_internal_security_policies", hook_retval(1))
create_ns_mutable_url_request(emu, s=url_str)

I understand it wrongly, the problem is solved, thank you