Skip to content

Instantly share code, notes, and snippets.

@0x75
Created June 29, 2013 15:23
Show Gist options
  • Save 0x75/5891528 to your computer and use it in GitHub Desktop.
Save 0x75/5891528 to your computer and use it in GitHub Desktop.
machdemo
/*
Copyright (c) 2003, Brian Alliet. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the author nor the names of contributors may be used
to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/thread_status.h>
#include <mach/exception.h>
#include <mach/task.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <mach/mach.h>
#include <mach/mach_init.h>
#include <mach/mach_traps.h>
#include <mach/mach_types.h>
#include <mach/mach_vm.h>
#include <mach/vm_map.h>
#include <mach/task.h>
#include <mach/task_info.h>
#include <mach/thread_status.h>
#include <mach/thread_info.h>
#include <mach/exception.h>
#include <mach/exception_types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <grp.h>
#include <sys/user.h>
#include <assert.h>
#include "mach_exc.h"
#define DIE(x) do { fprintf(stderr,"%s failed at %d\n",x,__LINE__); exit(1); } while(0)
#define ABORT(x) do { fprintf(stderr,"%s at %d\n",x,__LINE__); } while(0)
/* this is not specific to mach exception handling, its just here to separate required mach code from YOUR code */
static int my_handle_exn(char *addr, integer_t code);
/* These are not defined in any header, although they are documented */
extern boolean_t mach_exc_server(mach_msg_header_t *,mach_msg_header_t *);
extern kern_return_t exception_raise(
mach_port_t,mach_port_t,mach_port_t,
exception_type_t,exception_data_t,mach_msg_type_number_t);
extern kern_return_t exception_raise_state(
mach_port_t,mach_port_t,mach_port_t,
exception_type_t,exception_data_t,mach_msg_type_number_t,
thread_state_flavor_t*,thread_state_t,mach_msg_type_number_t,
thread_state_t,mach_msg_type_number_t*);
extern kern_return_t exception_raise_state_identity(
mach_port_t,mach_port_t,mach_port_t,
exception_type_t,exception_data_t,mach_msg_type_number_t,
thread_state_flavor_t*,thread_state_t,mach_msg_type_number_t,
thread_state_t,mach_msg_type_number_t*);
#define MAX_EXCEPTION_PORTS 16
///////////////////////////////////
char * Name = "fault";
pid_t pid = 38016;
task_t targetTask;
/*
pid_t get_pid(void) {
struct kinfo_proc *procs = NULL, *newprocs;
char thiscmd[MAXCOMLEN + 1];
pid_t thispid;
int mib[4];
size_t miblen;
int i, st, nprocs;
size_t size;
size = 0;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_ALL;
mib[3] = 0;
miblen = 3;
sysctl(mib, (unsigned int)miblen, NULL, &size, NULL, 0);
do {
size += size / 10;
newprocs = (kinfo_proc *) realloc(procs, size);
if (newprocs == 0) {
if (procs)
free(procs);
printf("could not reallocate memory");
}
procs = newprocs;
st = (int)sysctl(mib, (unsigned int)miblen, procs, &size, NULL, 0);
}
while (st == -1 && errno == ENOMEM);
nprocs = (int)size /sizeof(struct kinfo_proc);
for (i = 0; i < nprocs; i++) {
thispid = procs[i].kp_proc.p_pid;
strncpy(thiscmd, procs[i].kp_proc.p_comm, MAXCOMLEN);
thiscmd[MAXCOMLEN] = '\0';
if (strcmp(Name, thiscmd) == 0) {
return(thispid);
pid = thispid;
}
}
}
*/
void mach_assert( kern_return_t kr) {
if (kr != KERN_SUCCESS) {
mach_error("task_for_pid", kr);
exit(-1);
}
}
////////////////////////////////////
static mach_port_t exception_port;
static void exc_thread(void) {
puts("catching...\n");
mach_msg_return_t r;
/* These two structures contain some private kernel data. We don't need to
access any of it so we don't bother defining a proper struct. The
correct definitions are in the xnu source code. */
struct {
mach_msg_header_t head;
char data[256];
} reply;
struct {
mach_msg_header_t head;
mach_msg_body_t msgh_body;
char data[1024];
} msg;
for(;;) {
r = mach_msg(
&msg.head,
MACH_RCV_MSG|MACH_RCV_LARGE,
0,
sizeof(msg),
exception_port,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if(r != MACH_MSG_SUCCESS) DIE("mach_msg");
/* Handle the message (calls catch_exception_raise) */
if(!mach_exc_server(&msg.head,&reply.head)) DIE("exc_server");
/* Send the reply */
r = mach_msg(
&reply.head,
MACH_SEND_MSG,
reply.head.msgh_size,
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if(r != MACH_MSG_SUCCESS) DIE("mach_msg");
}
/* not reached */
}
static void exn_init() {
kern_return_t r;
mach_port_t me;
pthread_t thread;
pthread_attr_t attr;
exception_mask_t mask;
puts("attaching...\n");
mach_assert( task_for_pid(mach_task_self(), pid, &targetTask));
me = mach_task_self();
r = mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&exception_port);
if(r != MACH_MSG_SUCCESS) DIE("mach_port_allocate");
r = mach_port_insert_right(me,exception_port,exception_port,
MACH_MSG_TYPE_MAKE_SEND);
if(r != MACH_MSG_SUCCESS) DIE("mach_port_insert_right");
/* for others see mach/exception_types.h */
mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BREAKPOINT;
/* set the new exception ports */
r = task_set_exception_ports(targetTask,mask,exception_port,EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES,MACHINE_THREAD_STATE);
if(r != MACH_MSG_SUCCESS) DIE("task_set_exception_ports");
/*
if(pthread_attr_init(&attr) != 0) DIE("pthread_attr_init");
if(pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED) != 0)
DIE("pthread_attr_setdetachedstate");
if(pthread_create(&thread,&attr,exc_thread,NULL) != 0)
DIE("pthread_create");
pthread_attr_destroy(&attr);
*/
// set breakpoint
unsigned char bp;
uint64_t taddr = 0x0000000100000e8d;
puts("setting BPs\n");
mach_assert(mach_vm_protect(targetTask, taddr, 1, 0, VM_PROT_READ | VM_PROT_WRITE));
unsigned char BP = {0xCC};
mach_assert(mach_vm_write(targetTask, taddr, (vm_offset_t) &BP, 1));
mach_vm_size_t nread;
mach_vm_read_overwrite(targetTask, taddr, 1, (mach_vm_address_t) &bp, &nread);
if (bp == 0xCC)
puts("BP is set...\n");
else
puts("failed to set BP\n");
mach_assert( mach_vm_protect(targetTask, taddr, 1, 0, VM_PROT_READ | VM_PROT_EXECUTE));
exc_thread();
}
kern_return_t catch_mach_exception_raise(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,exception_data_t code,mach_msg_type_number_t code_count) {
kern_return_t r;
char *addr;
thread_state_flavor_t flavor = x86_EXCEPTION_STATE;
mach_msg_type_number_t exc_state_count = x86_EXCEPTION_STATE_COUNT;
x86_exception_state_t exc_state;
/* we should never get anything that isn't EXC_BAD_ACCESS, but just in case */
if(exception != EXC_BAD_ACCESS && exception != EXC_BREAKPOINT ) {
/* We aren't interested, pass it on to the old handler */
fprintf(stderr,"Exception: 0x%x Code: 0x%x 0x%x in catch....\n",
exception,
code_count > 0 ? code[0] : -1,
code_count > 1 ? code[1] : -1);
return 1;
}
if (exception == EXC_BREAKPOINT)
puts("BP catched \n");
else
puts("other Exception catched \n");
r = thread_get_state(thread,flavor, (natural_t*)&exc_state,&exc_state_count);
if (r != KERN_SUCCESS) DIE("thread_get_state");
/* This is the address that caused the fault */
addr = (char*) exc_state.ues.es64.__faultvaddr;
/* you could just as easily put your code in here, I'm just doing this to
point out the required code */
if(!my_handle_exn(addr, code[0])) return 1;
return KERN_SUCCESS;
}
kern_return_t catch_mach_exception_raise_state(mach_port_name_t exception_port,
int exception, exception_data_t code, mach_msg_type_number_t codeCnt,
int flavor, thread_state_t old_state, int old_stateCnt,
thread_state_t new_state, int new_stateCnt)
{
ABORT("catch_exception_raise_state");
return(KERN_INVALID_ARGUMENT);
}
kern_return_t catch_mach_exception_raise_state_identity(
mach_port_name_t exception_port, mach_port_t thread, mach_port_t task,
int exception, exception_data_t code, mach_msg_type_number_t codeCnt,
int flavor, thread_state_t old_state, int old_stateCnt,
thread_state_t new_state, int new_stateCnt)
{
ABORT("catch_exception_raise_state_identity");
return(KERN_INVALID_ARGUMENT);
}
static char *data;
static int my_handle_exn(char *addr, integer_t code) {
if(code == KERN_INVALID_ADDRESS) {
fprintf(stderr,"Got KERN_INVALID_ADDRESS at %p\n",addr);
exit(1);
}
if(code == KERN_PROTECTION_FAILURE) {
fprintf(stderr,"Got KERN_PROTECTION_FAILURE at %p\n",addr);
if(addr == NULL) {
fprintf(stderr,"Tried to dereference NULL");
exit(1);
}
if(addr == data) {
fprintf(stderr,"Making data (%p) writeable\n",addr);
if(mprotect(addr,4096,PROT_READ|PROT_WRITE) < 0) DIE("mprotect");
return 1; // we handled it
}
fprintf(stderr,"Got KERN_PROTECTION_FAILURE at %p\n",addr);
return 0; // forward it
}
/* You should filter out anything you don't want in the catch_exception_raise... above
and forward it */
fprintf(stderr,"Got unknown code %d at %p\n",(int)code,addr);
return 0;
}
int main(int argc, char *argv[]) {
/* fire up the exception thread */
exn_init();
/* data = valloc(4096);
if(data == 0) DIE("valloc");
if(mprotect(data,4096,PROT_READ) < 0) DIE("mprotect");
// this will be fixed
strcpy(data,"foo");
printf("%s\n",data);
// these will each exit
// this will throw a KERN_INVALID_ADDRESS
printf("%d",*((int*)0xdeadbeef));
//this will throw a KERN_PROTECTION_FAILURE
printf("%d",*((int*)0));
*/
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment