bool ksbt_isBacktraceTooLong(const STRUCT_MCONTEXT_L* const machineContext, int maxLength) { const uintptr_t instructionAddress = ksmach_instructionAddress(machineContext); if(instructionAddress == 0) { return 0; } KSFrameEntry frame = {0}; const uintptr_t framePtr = ksmach_framePointer(machineContext); if(framePtr == 0 || ksmach_copyMem((void*)framePtr, &frame, sizeof(frame)) != KERN_SUCCESS) { return 1; } for(int i = 1; i < maxLength; i++) { if(frame.previous == 0 || ksmach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) { return false; } } return true; }
const char* ksobjc_className(void* address) { ObjCObjectType objectType = ksobjc_objectType(address); class_t* pIsa = address; class_t cls; class_rw_t rw; class_ro_t ro; char name[128]; if(objectType == kObjCObjectTypeNone) { return NULL; } if(objectType == kObjCObjectTypeObject) { // Copy the object's isa pointer if(ksmach_copyMem(pIsa, &pIsa, sizeof(pIsa)) != KERN_SUCCESS) { return NULL; } } // We're now guaranteed that pClass points to a class. if(ksmach_copyMem(pIsa, &cls, sizeof(cls)) != KERN_SUCCESS) { return NULL; } if(ksmach_copyMem(cls.data, &rw, sizeof(rw)) != KERN_SUCCESS) { return NULL; } if(ksmach_copyMem(rw.ro, &ro, sizeof(ro)) != KERN_SUCCESS) { return NULL; } size_t nameLength = ksmach_copyMaxPossibleMem(ro.name, name, sizeof(name)); if(ro.name + nameLength < ro.name) { // Wrapped around address space. return NULL; } if(nameLength == 0 || !isValidClassNameStartChar(*name)) { return NULL; } for(size_t i = 0; i < nameLength; i++) { if(!isValidClassNameChar(name[i])) { if(name[i] == 0) { return ro.name; } return NULL; } } return NULL; }
int ksbt_backtraceLength(const STRUCT_MCONTEXT_L* const machineContext) { const uintptr_t instructionAddress = ksmach_instructionAddress(machineContext); if(instructionAddress == 0) { return 0; } KSFrameEntry frame = {0}; const uintptr_t framePtr = ksmach_framePointer(machineContext); if(framePtr == 0 || ksmach_copyMem((void*)framePtr, &frame, sizeof(frame)) != KERN_SUCCESS) { return 1; } for(int i = 1; i < kBacktraceGiveUpPoint; i++) { if(frame.previous == 0 || ksmach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) { return i; } } return kBacktraceGiveUpPoint; }
int ksbt_backtraceThreadState(const STRUCT_MCONTEXT_L* const machineContext, uintptr_t*const backtraceBuffer, const int skipEntries, const int maxEntries) { const uintptr_t instructionAddress = ksmach_instructionAddress(machineContext); if(maxEntries == 0) { return 0; } int startPoint = 0; if(skipEntries == 0) { backtraceBuffer[0] = instructionAddress; if(maxEntries == 1) { return 1; } startPoint = 1; } KSFrameEntry frame = {0}; const uintptr_t framePtr = ksmach_framePointer(machineContext); if(framePtr == 0 || ksmach_copyMem((void*)framePtr, &frame, sizeof(frame)) != KERN_SUCCESS) { return 0; } for(int i = 1; i < skipEntries; i++) { if(frame.previous == 0 || ksmach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) { return 0; } } int i; for(i = startPoint; i < maxEntries; i++) { backtraceBuffer[i] = DETAG_FRAME_CALLER_ADDRESS(frame.caller); if(backtraceBuffer[i] == 0 || frame.previous == 0 || ksmach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) { break; } } return i; }
bool ksmach_getThreadQueueName(const thread_t thread, char* const buffer, size_t bufLength) { struct internal_dispatch_queue_s* pQueue; struct internal_dispatch_queue_s queue; if(bufLength > sizeof(queue.dq_label)) { bufLength = sizeof(queue.dq_label); } // Recast the opaque thread to our hacky internal thread structure pointer. const pthread_t pthread = pthread_from_mach_thread_np(thread); const internal_pthread_t const threadStruct = (internal_pthread_t)pthread; if(ksmach_copyMem(&threadStruct->tsd[dispatch_queue_key], &pQueue, sizeof(pQueue)) != KERN_SUCCESS) { KSLOG_TRACE("Could not copy queue pointer from %p", &threadStruct->tsd[dispatch_queue_key]); return false; } if(pQueue == NULL) { KSLOG_TRACE("Queue pointer is NULL"); return false; } if(ksmach_copyMem(pQueue, &queue, sizeof(queue)) != KERN_SUCCESS) { KSLOG_TRACE("Could not copy queue data from %p", pQueue); return false; } // Queue label must be a null terminated string. int iLabel; for(iLabel = 0; iLabel < (int)sizeof(queue.dq_label); iLabel++) { if(queue.dq_label[iLabel] < ' ' || queue.dq_label[iLabel] > '~') { break; } } if(queue.dq_label[iLabel] != 0) { // Found a non-null, invalid char. KSLOG_TRACE("Queue label contains invalid chars"); return false; } strncpy(buffer, queue.dq_label, bufLength); KSLOG_TRACE("Queue label = %s", buffer); return true; }
size_t ksmach_copyMaxPossibleMem(const void* const src, void* const dst, const size_t numBytes) { const uint8_t* pSrc = src; const uint8_t* pSrcMax = (uint8_t*)src + numBytes; const uint8_t* pSrcEnd = (uint8_t*)src + numBytes; uint8_t* pDst = dst; size_t bytesCopied = 0; // Short-circuit if no memory is readable if(ksmach_copyMem(src, dst, 1) != KERN_SUCCESS) { return 0; } else if(numBytes <= 1) { return numBytes; } for(;;) { ssize_t copyLength = pSrcEnd - pSrc; if(copyLength <= 0) { break; } if(ksmach_copyMem(pSrc, pDst, (size_t)copyLength) == KERN_SUCCESS) { bytesCopied += (size_t)copyLength; pSrc += copyLength; pDst += copyLength; pSrcEnd = pSrc + (pSrcMax - pSrc) / 2; } else { if(copyLength <= 1) { break; } pSrcMax = pSrcEnd; pSrcEnd = pSrc + copyLength / 2; } } return bytesCopied; }
thread_t ksmach_machThreadFromPThread(const pthread_t pthread) { const internal_pthread_t threadStruct = (internal_pthread_t)pthread; thread_t machThread = 0; if(ksmach_copyMem(&threadStruct->kernel_thread, &machThread, sizeof(machThread)) != KERN_SUCCESS) { KSLOG_TRACE("Could not copy mach thread from %p", threadStruct->kernel_thread); return 0; } return machThread; }
pthread_t ksmach_pthreadFromMachThread(const thread_t thread) { internal_pthread_t threadStruct = (internal_pthread_t)g_topThread; thread_t machThread = 0; for(int i = 0; i < 50; i++) { if(ksmach_copyMem(&threadStruct->kernel_thread, &machThread, sizeof(machThread)) != KERN_SUCCESS) { break; } if(machThread == thread) { return (pthread_t)threadStruct; } if(ksmach_copyMem(&threadStruct->plist.tqe_next, &threadStruct, sizeof(threadStruct)) != KERN_SUCCESS) { break; } } return 0; }
bool ksmach_getThreadName(const thread_t thread, char* const buffer, size_t bufLength) { const pthread_t pthread = ksmach_pthreadFromMachThread(thread); const internal_pthread_t threadStruct = (internal_pthread_t)pthread; size_t copyLength = bufLength > MAXTHREADNAMESIZE ? MAXTHREADNAMESIZE : bufLength; if(ksmach_copyMem(threadStruct->pthread_name, buffer, copyLength) != KERN_SUCCESS) { KSLOG_TRACE("Could not copy thread name from %p", threadStruct->pthread_name); return false; } buffer[copyLength-1] = 0; return true; }
ObjCObjectType ksobjc_objectType(void* self) { /* How to determine if it's a class or object. Root object/class: - class: self->isa->superclass == self - object: self->isa->superclass == nil Non-root object/class: - class: self->isa->isa->isa == self->isa->isa - object: self->isa->isa->isa->isa == self->isa->isa->isa */ class_t* pIsa; class_t cls; if(self == NULL) { return kObjCObjectTypeNone; } // Get the object/class isa pointer if(ksmach_copyMem(self, &pIsa, sizeof(pIsa)) != KERN_SUCCESS) { return kObjCObjectTypeNone; } // Copy the class contents if(ksmach_copyMem(pIsa, &cls, sizeof(cls)) != KERN_SUCCESS) { return kObjCObjectTypeNone; } // Simple case: Root object or class. if(cls.superclass == NULL) { return kObjCObjectTypeObject; } if(cls.superclass == self) { return kObjCObjectTypeClass; } // One more isa before loop = class pIsa = cls.isa; if(ksmach_copyMem(pIsa, &cls, sizeof(cls)) != KERN_SUCCESS) { return kObjCObjectTypeNone; } if(cls.isa == pIsa) { return kObjCObjectTypeClass; } // Two more isa before loop = object pIsa = cls.isa; if(ksmach_copyMem(pIsa, &cls, sizeof(cls)) != KERN_SUCCESS) { return kObjCObjectTypeNone; } if(cls.isa == pIsa) { return kObjCObjectTypeObject; } // Don't know what this is return kObjCObjectTypeNone; }
int ksbt_backtraceThreadState(const STRUCT_MCONTEXT_L* const machineContext, uintptr_t*const backtraceBuffer, const int skipEntries, const int maxEntries) { if(maxEntries == 0) { return 0; } int i = 0; if(skipEntries == 0) { const uintptr_t instructionAddress = ksmach_instructionAddress(machineContext); backtraceBuffer[i] = instructionAddress; i++; if(i == maxEntries) { return i; } } if(skipEntries <= 1) { uintptr_t linkRegister = ksmach_linkRegister(machineContext); if(linkRegister) { backtraceBuffer[i] = linkRegister; i++; if (i == maxEntries) { return i; } } } KSFrameEntry frame = {0}; const uintptr_t framePtr = ksmach_framePointer(machineContext); if(framePtr == 0 || ksmach_copyMem((void*)framePtr, &frame, sizeof(frame)) != KERN_SUCCESS) { return 0; } for(int j = 1; j < skipEntries; j++) { if(frame.previous == 0 || ksmach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) { return 0; } } for(; i < maxEntries; i++) { backtraceBuffer[i] = frame.return_address; if(backtraceBuffer[i] == 0 || frame.previous == 0 || ksmach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) { break; } } return i; }