├── .gitignore ├── README.md ├── ciokitlib.pxd ├── iokit_test.py ├── iokitlib.pyx └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.c 3 | *.so 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # coralsun 2 | 3 | Coralsun is a small utility cython library used to provide python support for low level kernel features. 4 | 5 | Currently only IOKit and Mach IPC is supported. The fuzzer opalrobot depends on this utility library. 6 | 7 | Support for Mach IPC messaging is partially broken and under development currently. 8 | 9 | Version 0.1 10 | 11 |

Dependencies

12 | 13 | * pip install cython 14 | 15 |

Build Instructions

16 | 17 | ```python setup.py build_ext --inplace``` 18 | 19 |

Installation

20 | 21 | ```python setup.py install``` 22 | 23 |

API Usage

24 | 25 | Examples of usage are included in the iokit_test.py unit test script. -------------------------------------------------------------------------------- /ciokitlib.pxd: -------------------------------------------------------------------------------- 1 | 2 | cdef extern from "mach/vm_region.h" nogil: 3 | ctypedef void *vm_region_info_t; 4 | 5 | cdef extern from "mach/mach.h" nogil: 6 | ctypedef int boolean_t 7 | ctypedef int mach_msg_return_t 8 | ctypedef int mach_msg_option_t 9 | ctypedef int mach_msg_size_t 10 | ctypedef int mach_msg_timeout_t 11 | ctypedef int mach_msg_bits_t 12 | ctypedef int mach_port_seqno_t 13 | ctypedef int mach_msg_id_t 14 | ctypedef int mach_port_t 15 | ctypedef int mach_port_name_t 16 | ctypedef int mach_port_right_t 17 | ctypedef int mach_msg_trailer_type_t 18 | ctypedef int mach_msg_trailer_size_t 19 | ctypedef int kern_return_t 20 | ctypedef int ipc_space_t 21 | ctypedef int mach_msg_type_name_t 22 | ctypedef int mach_msg_copy_options_t 23 | ctypedef int mach_msg_descriptor_type_t 24 | ctypedef int mach_msg_type_number_t 25 | ctypedef unsigned int * uintptr_t; 26 | 27 | ctypedef unsigned long mach_vm_address_t; 28 | ctypedef unsigned long mach_vm_size_t; 29 | ctypedef unsigned long vm_size_t; 30 | ctypedef unsigned long vm_address_t; 31 | 32 | ctypedef mach_port_t vm_map_t; 33 | ctypedef int vm_region_flavor_t; 34 | 35 | ctypedef mach_port_t memory_object_name_t; 36 | 37 | ctypedef struct mach_msg_header_t: 38 | mach_msg_bits_t msgh_bits 39 | mach_msg_size_t msgh_size 40 | mach_port_t msgh_remote_port 41 | mach_port_t msgh_local_port 42 | mach_port_name_t msgh_voucher_port 43 | mach_msg_id_t msgh_id 44 | 45 | mach_msg_return_t mach_msg(mach_msg_header_t *msg, mach_msg_option_t option, mach_msg_size_t send_size, 46 | mach_msg_size_t rcv_size, mach_port_t rcv_name, mach_msg_timeout_t timeout, 47 | mach_port_t notify) 48 | 49 | mach_msg_return_t mach_msg_send(mach_msg_header_t *msg) 50 | 51 | kern_return_t vm_region_64(vm_map_t target_task, vm_address_t *address, vm_size_t *size, vm_region_flavor_t flavor, vm_region_info_t info, mach_msg_type_number_t *infoCnt, mach_port_t *object_name); 52 | 53 | cdef extern from "mach/task.h" nogil: 54 | ctypedef unsigned int uint32_t 55 | ctypedef unsigned long uint64_t 56 | ctypedef int int_32_t 57 | ctypedef uint32_t natural_t 58 | ctypedef int_32_t integer_t 59 | ctypedef mach_port_t task_t 60 | ctypedef mach_port_t task_port_t 61 | 62 | cdef extern from "IOKit/IOTypes.h" nogil: 63 | ctypedef mach_port_t io_object_t 64 | ctypedef io_object_t io_service_t 65 | ctypedef io_object_t io_connect_t 66 | ctypedef io_object_t io_enumerator_t 67 | ctypedef io_object_t io_iterator_t 68 | ctypedef io_object_t io_registry_entry_t 69 | ctypedef uint32_t IOOptionBits; 70 | 71 | cdef extern from "CoreFoundation/CFDictionary.h" nogil: 72 | ctypedef void* CFDictionaryRef 73 | ctypedef void* CFMutableDictionaryRef 74 | 75 | cdef extern from "CoreFoundation/CFString.h" nogil: 76 | ctypedef void* CFStringRef 77 | 78 | cdef extern from "CoreFoundation/CoreFoundation.h" nogil: 79 | ctypedef void* CFTypeRef 80 | ctypedef void* CFAllocatorRef 81 | ctypedef unsigned int CFStringEncoding 82 | cdef void CFRelease(void * cf) 83 | cdef CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, char *cStr, CFStringEncoding encoding) 84 | 85 | cdef extern from "mach/mach_init.h" nogil: 86 | mach_port_t mach_task_self() 87 | mach_port_t mach_thread_self() 88 | 89 | cdef extern from "IOKit/IOKitLib.h" nogil: 90 | kern_return_t IOServiceOpen(io_service_t service, task_port_t owningTask, uint32_t type, io_connect_t * connect ) 91 | io_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching); 92 | kern_return_t IOServiceGetMatchingServices(mach_port_t masterPort, CFDictionaryRef matching, io_iterator_t *existing) 93 | CFMutableDictionaryRef IOServiceNameMatching(const char *name) 94 | CFMutableDictionaryRef IOServiceMatching(const char *name) 95 | io_object_t IOIteratorNext(io_iterator_t iterator) 96 | kern_return_t IOObjectRelease(io_object_t object) 97 | kern_return_t IOConnectCallMethod(mach_port_t connection, uint32_t selector, const uint64_t *input, uint32_t inputCnt, const void *inputStruct, size_t inputStructCnt, uint64_t *output, uint32_t *outputCnt, void *outputStruct, size_t *outputStructCnt); 98 | kern_return_t IOServiceClose(io_connect_t connect); 99 | kern_return_t IOConnectMapMemory(io_connect_t connect, uint32_t memoryType, task_port_t intoTask, mach_vm_address_t *atAddress, mach_vm_size_t *ofSize, IOOptionBits options); 100 | kern_return_t IOConnectUnmapMemory(io_connect_t connect, uint32_t memoryType, task_port_t fromTask, mach_vm_address_t atAddress); 101 | kern_return_t IOConnectAddClient(io_connect_t connect, io_connect_t client); 102 | kern_return_t IOConnectSetCFProperty(io_connect_t connect, CFStringRef propertyName, CFTypeRef property); 103 | kern_return_t IORegistryEntrySetCFProperty(io_registry_entry_t entry, CFStringRef propertyName, CFTypeRef property); 104 | kern_return_t IOConnectTrap0(io_connect_t connect, uint32_t index); 105 | kern_return_t IOConnectTrap1(io_connect_t connect, uint32_t index, uint32_t p1); 106 | kern_return_t IOConnectTrap2(io_connect_t connect, uint32_t index, uint32_t p1, uint32_t p2); 107 | kern_return_t IOConnectTrap3(io_connect_t connect, uint32_t index, uint32_t p1, uint32_t p2, uint32_t p3); 108 | kern_return_t IOConnectTrap4(io_connect_t connect, uint32_t index, uint32_t p1, uint32_t p2, uint32_t p3, uint32_t p4); 109 | kern_return_t IOConnectTrap5(io_connect_t connect, uint32_t index, uint32_t p1, uint32_t p2, uint32_t p3, uint32_t p4, uint32_t p5); 110 | kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uint32_t p1, uint32_t p2, uint32_t p3, uint32_t p4, uint32_t p5, uint32_t p6); 111 | kern_return_t IOConnectSetNotificationPort(io_connect_t connect, uint32_t type, mach_port_t port, uintptr_t reference); 112 | kern_return_t IOConnectGetService(io_connect_t connect, io_service_t *service); 113 | kern_return_t IOConnectCallAsyncMethod(mach_port_t connection, uint32_t selector, mach_port_t wake_port, uint64_t *reference, uint32_t referenceCnt, const uint64_t *input, uint32_t inputCnt, const void *inputStruct, size_t inputStructCnt, uint64_t *output, uint32_t *outputCnt, void *outputStruct, size_t *outputStructCnt); 114 | 115 | cdef extern from "servers/bootstrap.h" nogil: 116 | mach_port_t bootstrap_port 117 | kern_return_t bootstrap_look_up(mach_port_t bp, const char * service_name, mach_port_t *sp) 118 | 119 | cdef extern from "mach/vm_prot.h" nogil: 120 | ctypedef int vm_prot_t; 121 | 122 | cdef extern from "mach/vm_inherit.h" nogil: 123 | ctypedef unsigned int vm_inherit_t; 124 | 125 | cdef extern from "mach/vm_behavior.h" nogil: 126 | ctypedef int vm_behavior_t; 127 | 128 | cdef extern from "mach/vm_region.h" nogil: 129 | cdef struct vm_region_basic_info: 130 | vm_prot_t protection; 131 | vm_prot_t max_protection; 132 | vm_inherit_t inheritance; 133 | boolean_t shared; 134 | boolean_t reserved; 135 | unsigned int offset; 136 | vm_behavior_t behavior; 137 | unsigned short user_wired_count; 138 | 139 | ctypedef void* vm_region_basic_info_t; 140 | ctypedef void* vm_region_basic_info_data_t; 141 | ctypedef vm_region_basic_info_data_t vm_region_info_t 142 | 143 | 144 | -------------------------------------------------------------------------------- /iokit_test.py: -------------------------------------------------------------------------------- 1 | 2 | import iokitlib 3 | import unittest 4 | import binascii 5 | 6 | class IOKitTest(unittest.TestCase): 7 | 8 | def test_open_service_valid(self): 9 | iokit = iokitlib.iokit() 10 | h = iokit.open_service("IOHIDSystem",1) 11 | self.assertNotEqual(h, 0) 12 | 13 | def test_open_service_failed(self): 14 | iokit = iokitlib.iokit() 15 | h = iokit.open_service("blah",0) 16 | self.assertEqual(h, 0) 17 | 18 | def test_connect_call_method_scalar_noinput_structoutput_valid(self): 19 | iokit = iokitlib.iokit() 20 | h = iokit.open_service("IOSurfaceRoot",0) 21 | 22 | input_scalar = None 23 | input_struct = None 24 | 25 | output_scalar = None 26 | output_struct = b"0" * 20 # 20 bytes output 27 | 28 | selector = 0x0d 29 | 30 | kr = iokit.connect_call_method(h,selector,input_scalar,input_struct,output_scalar,output_struct) 31 | 32 | self.assertEqual(kr,0) 33 | 34 | print("Output struct = ") 35 | hexstr = ':'.join(x.encode('hex') for x in output_struct) 36 | print(hexstr) 37 | #print("test_connect_call_method_scalar_valid %d " % kr) 38 | 39 | @unittest.skip("Gen6Accelerator is not available on 10.12.3") 40 | def test_connect_call_method_scalar_input_structoutput_valid(self): 41 | iokit = iokitlib.iokit() 42 | h = iokit.open_service("Gen6Accelerator",1) 43 | 44 | input_scalar = [0] # One element 45 | input_struct = None 46 | 47 | output_scalar = None 48 | output_struct = b"\x00" * 28 49 | 50 | selector = 0x10 51 | 52 | kr = iokit.connect_call_method(h,selector,input_scalar,input_struct,output_scalar,output_struct) 53 | self.assertEqual(kr,0) 54 | 55 | # Print the output 56 | print("Output struct = ") 57 | hexstr = ':'.join(x.encode('hex') for x in output_struct) 58 | print(hexstr) 59 | 60 | def test_connect_call_method_struct_input_struct_output(self): 61 | iokit = iokitlib.iokit() 62 | h = iokit.open_service("IntelAccelerator",5) 63 | 64 | input_scalar = None 65 | 66 | input_struct = binascii.unhexlify("3f0000000100000000000000000000000000000000000000") 67 | print(input_struct) 68 | 69 | output_scalar = None 70 | output_struct = binascii.unhexlify("3f0000000100000000000000000000000000000000000000") 71 | 72 | selector = 11 73 | 74 | kr = iokit.connect_call_method(h,selector,input_scalar,input_struct,output_scalar,output_struct) 75 | self.assertEqual(kr,0) 76 | 77 | # Print the output 78 | print("Output struct = ") 79 | hexstr = ':'.join(x.encode('hex') for x in output_struct) 80 | print(hexstr) 81 | 82 | 83 | def test_connect_call_method_struct_input_struct_output(self): 84 | iokit = iokitlib.iokit() 85 | h = iokit.open_service("IntelAccelerator",5) 86 | 87 | input_scalar = None 88 | 89 | input_struct = binascii.unhexlify("3f0000000100000000000000000000000000000000000000") 90 | print(input_struct) 91 | 92 | output_scalar = None 93 | output_struct = binascii.unhexlify("3f0000000100000000000000000000000000000000000000") 94 | 95 | selector = 11 96 | 97 | print("++ making async call ++") 98 | kr = iokit.connect_call_async_method(h,selector,input_scalar,input_struct,output_scalar,output_struct) 99 | self.assertEqual(kr,0) 100 | 101 | # Print the output 102 | print("Output struct = ") 103 | hexstr = ':'.join(x.encode('hex') for x in output_struct) 104 | print(hexstr) 105 | 106 | def test_mach_msg_send_valid_bootstrap(self): 107 | iokit = iokitlib.iokit() 108 | data = binascii.unhexlify("13151300d000000007080000031f0000030c000000000010214350580500000000f00000a80000000700000073756273797374656d00000000400000030000000000000068616e646c650000004000000000000000000000726f7574696e6500004000002403000000000000666c6167730000000040000000000000000000006e616d65000000000090000010000000636f6d2e6170706c652e666f6e74730074797065000000000040000007000000000000006c6f6f6b75702d68616e646c65000000004000000000000000000000") 109 | service = "TASK_BOOTSTRAP_PORT" 110 | 111 | ret = iokit.send_mach_msg(service,data) 112 | print(ret) 113 | 114 | self.assertEqual(ret,0) 115 | 116 | def test_send_coreserviced(self): 117 | iokit = iokitlib.iokit() 118 | data = binascii.unhexlify("13151300010000000b4b000007070000030c000023270000000000000100000001000000435353656564000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010001000000") 119 | service = "com.apple.CoreServices.coreservicesd" 120 | 121 | ret = iokit.send_mach_msg(service,data) 122 | print(ret) 123 | 124 | #self.assertEqual(ret,0) 125 | 126 | def test_send_audiohald(self): 127 | iokit = iokitlib.iokit() 128 | data = binascii.unhexlify("13151300380000000379000007070000030c000063690f00000000000100000001000000236b6c63626f6c6700000000") 129 | service = "com.apple.audio.audiohald" 130 | 131 | ret = iokit.send_mach_msg(service,data) 132 | print(ret) 133 | 134 | self.assertEqual(ret,0) 135 | 136 | 137 | def test_mapsharedmemory_ok(self): 138 | iokit = iokitlib.iokit() 139 | h = iokit.open_service("IOFramebuffer",1) 140 | 141 | print("connect shared memory handle: " + str(h)) 142 | self.assertNotEqual(h, 0) 143 | 144 | # This call returns back the memory address mapped. 145 | kr = iokit.map_sharedmemory(h,110,4096) 146 | print("sharedmemory: " + str(hex(kr))) 147 | 148 | self.assertNotEqual(kr,0) 149 | 150 | def test_mapsharedmemory_fail(self): 151 | iokit = iokitlib.iokit() 152 | 153 | h = iokit.open_service("IOAccelerator",1) 154 | 155 | print("connect shared memory handle: " + str(h)) 156 | self.assertNotEqual(h, 0) 157 | 158 | # This call should fail and therefore the address should be zero. 159 | kr = iokit.map_sharedmemory(h,110,4096) 160 | print("sharedmemory: " + str(hex(kr))) 161 | 162 | self.assertEqual(kr,0) 163 | 164 | def test_mapsharedmemorydump_ok(self): 165 | iokit = iokitlib.iokit() 166 | h = iokit.open_service("IOFramebuffer",1) 167 | 168 | print("connect shared memory handle: " + str(h)) 169 | self.assertNotEqual(h, 0) 170 | 171 | # This call returns back the memory address mapped. 172 | kr = iokit.map_sharedmemory(h,110,4096) 173 | 174 | iokit.set_sharedmemory(kr,"BBBBB") 175 | 176 | memory = iokit.dump_sharedmemory(kr,4096) 177 | 178 | print(binascii.hexlify(memory)) 179 | 180 | 181 | def test_ioconnect_setproperty_ok(self): 182 | iokit = iokitlib.iokit() 183 | 184 | h = iokit.open_service("IOPMrootDomain",0) 185 | self.assertNotEqual(h, 0) 186 | 187 | kr = iokit.ioconnect_setproperty(h,"Hibernate File","TEST") 188 | 189 | print("ioconnect_setproperty ret = %d" % kr) 190 | 191 | #self.assertEqual(kr,0) 192 | 193 | def test_ioregistry_setproperty(self): 194 | iokit = iokitlib.iokit() 195 | 196 | kr = iokit.ioregistry_setproperty("IOPMrootDomain","Hibernate File","TEST") 197 | 198 | print("ioconnect_setproperty ret = %d" % kr) 199 | 200 | self.assertEqual(kr,0) 201 | 202 | """ This should test writing to a shared memory map """ 203 | def test_writable_mapping(self): 204 | iokit = iokitlib.iokit() 205 | h = iokit.open_service("IOFramebuffer",1) 206 | kr = iokit.map_sharedmemory(h,110,4096) 207 | if kr == 0: 208 | print("++ mapping failed ++") 209 | else: 210 | 211 | writable = iokit.is_memory_writable(kr) 212 | if writable: 213 | iokit.set_sharedmemory(kr,"BBBBB") 214 | memory = iokit.dump_sharedmemory(kr,4096) 215 | print(binascii.hexlify(memory)) 216 | 217 | """ 218 | This should test writing to a read only memory map 219 | The writable test should failt, otherwise we could cause a segfault. 220 | """ 221 | def test_readonly_mapping(self): 222 | iokit = iokitlib.iokit() 223 | h = iokit.open_service("IOFramebuffer",1) 224 | kr = iokit.map_sharedmemory(h,100,4096) 225 | if kr == 0: 226 | print("++ mapping failed ++") 227 | else: 228 | writable = iokit.is_memory_writable(kr) 229 | if writable: 230 | iokit.set_sharedmemory(kr,"BBBBB") 231 | memory = iokit.dump_sharedmemory(kr,4096) 232 | print(binascii.hexlify(memory)) 233 | 234 | """ 235 | Determine if a memory page is writable 236 | """ 237 | def test_writable_memory(self): 238 | iokit = iokitlib.iokit() 239 | iokit.is_memory_writable(0xffffffff) 240 | 241 | """ Try set the notification port for a service """ 242 | def test_set_notification_port(self): 243 | iokit = iokitlib.iokit() 244 | h = iokit.open_service("IOFramebuffer",1) 245 | t = 1 246 | kr = iokit.ioconnect_setnotificationport(h,t,1) 247 | print("++ set notification port %d ++" % kr) 248 | 249 | """ Try get the ioservice a connect handle was opened on """ 250 | def test_get_service(self): 251 | iokit = iokitlib.iokit() 252 | h = iokit.open_service("IOFramebuffer",1) 253 | kr = iokit.ioconnect_getservice(h) 254 | print("++ get_service: %d ++" % kr) 255 | 256 | if __name__ == '__main__': 257 | unittest.main() -------------------------------------------------------------------------------- /iokitlib.pyx: -------------------------------------------------------------------------------- 1 | cimport cython 2 | 3 | from ciokitlib cimport * 4 | from libc.stdlib cimport malloc, free 5 | from libc.string cimport memcpy, memset, strncpy, strlen 6 | 7 | import binascii 8 | 9 | cdef class iokit: 10 | 11 | def open_service(self,service_name,userclient_type): 12 | cdef kern_return_t kr = -1 13 | cdef io_iterator_t iterator = 0 14 | cdef io_connect_t conn = 0 15 | cdef io_service_t svc = 0 16 | cdef CFDictionaryRef matching 17 | 18 | matching = IOServiceMatching(service_name) 19 | svc = IOServiceGetMatchingService(0,matching) 20 | 21 | print("SVC " + str(svc)) 22 | 23 | if (svc == 0): 24 | print("++ IOServiceGetMatchingService could not find service for %s" % service_name) 25 | matching = IOServiceNameMatching(service_name) 26 | svc = IOServiceGetMatchingService(0, matching); 27 | 28 | if (svc == 0): 29 | # TODO: Try to loop for a partial match 30 | print("++ Looking for a partial match ++") 31 | matching = IOServiceMatching("IOService") 32 | IOServiceGetMatchingServices(0, matching, &iterator) 33 | svc = IOIteratorNext(iterator); 34 | 35 | kr = IOServiceOpen(svc, mach_task_self(), userclient_type, &conn); 36 | print("IOServiceOpen kr: %d" % kr) 37 | 38 | IOObjectRelease(svc); 39 | IOObjectRelease(iterator); 40 | 41 | return conn 42 | 43 | def close_service(self,handle): 44 | cdef kern_return_t kr = -1 45 | kr = IOServiceClose(handle); 46 | return kr 47 | 48 | def connect_call_method(self,conn,selector,scalar_input,structInput,scalar_output,struct_output): 49 | print("Connect call method called") 50 | cdef uint64_t *input_scalar 51 | cdef uint32_t input_scalar_size 52 | cdef uint32_t output_scalar_size 53 | cdef size_t inputStructCnt 54 | cdef size_t outputStructCnt 55 | #cdef bytes input_struct 56 | #cdef const void *inputStruct 57 | cdef char *outputStruct 58 | cdef bytes py_bytes 59 | cdef kern_return_t kr = -1 60 | cdef uint64_t *output_scalar 61 | cdef char* inputStruct 62 | 63 | # Handle the input scalar first. 64 | if type(scalar_input) is list: 65 | print("++ An input list was passed ++") 66 | 67 | input_scalar_len = len(scalar_input) 68 | print("input scalar len = %d" % input_scalar_len) 69 | 70 | input_scalar_size = input_scalar_len 71 | i = 0 72 | input_scalar = malloc(input_scalar_size * sizeof(uint64_t)) 73 | if not input_scalar: 74 | raise MemoryError() 75 | 76 | # Convert the python array to native. 77 | for elem in scalar_input: 78 | if isinstance(elem, (int, long)): 79 | input_scalar[i] = elem 80 | i += 1 81 | else: 82 | print("++ Scalar input is None or type we cannot convert ++") 83 | input_scalar = NULL 84 | input_scalar_size = 0 85 | 86 | # Now handle the input struct types 87 | if isinstance(structInput, basestring): 88 | print("A string was passed") 89 | #hexstr = ':'.join(x.encode('hex') for x in structInput) 90 | #print(hexstr) 91 | 92 | inputStructCnt = len(structInput) 93 | print("++ Input struct size = " + str(hex(inputStructCnt))) 94 | 95 | # First convert the python string to char * string to get at raw data 96 | inputStruct = structInput 97 | else: 98 | print("++ Struct input is None or type we cannot convert ++") 99 | inputStruct = NULL 100 | inputStructCnt = 0 101 | 102 | # Handle the output scalar here 103 | if type(scalar_output) is list: 104 | print("++ An output list was passed ++") 105 | output_scalar_len = len(scalar_output) 106 | print("output scalar len = %d" % output_scalar_len) 107 | output_scalar_size = output_scalar_len 108 | 109 | output_scalar = malloc(output_scalar_size * sizeof(uint64_t)) 110 | if not output_scalar: 111 | raise MemoryError() 112 | else: 113 | print("++ Scalar output is None or type we cannot convert ++ ") 114 | output_scalar = NULL 115 | output_scalar_size = 0 116 | 117 | # Now handle he output struct. 118 | if isinstance(struct_output, basestring): 119 | print("++ An output string has been passed ++") 120 | outputStructCnt = len(struct_output) 121 | print("Output struct cnt = " + str(outputStructCnt)) 122 | 123 | # Convert our byte string to char * string to get at raw data. 124 | outputStruct = struct_output 125 | else: 126 | print("++ Struct output is None or type we cannot convert ++") 127 | outputStruct = NULL 128 | outputStructCnt = 0 129 | 130 | # Finally make the call 131 | print("%d,%d,%d,%d,%d" % (selector,input_scalar_size,inputStructCnt,output_scalar_size,outputStructCnt)) 132 | kr = IOConnectCallMethod(conn,selector,input_scalar,input_scalar_size,inputStruct,inputStructCnt,output_scalar,&output_scalar_size,outputStruct,&outputStructCnt) 133 | 134 | # Scalar output 135 | if output_scalar_size: 136 | print("++ Converting output scalar ++") 137 | 138 | # Struct output 139 | if outputStructCnt: 140 | pass 141 | 142 | # Cleanup our temp buffers. 143 | if input_scalar: 144 | free(input_scalar) 145 | 146 | if output_scalar: 147 | free(output_scalar) 148 | 149 | return kr 150 | 151 | def connect_call_async_method(self,conn,selector,scalar_input,structInput,scalar_output,struct_output): 152 | print("Connect call method called") 153 | cdef uint64_t *input_scalar 154 | cdef uint32_t input_scalar_size 155 | cdef uint32_t output_scalar_size 156 | cdef size_t inputStructCnt 157 | cdef size_t outputStructCnt 158 | #cdef bytes input_struct 159 | #cdef const void *inputStruct 160 | cdef char *outputStruct 161 | cdef bytes py_bytes 162 | cdef kern_return_t kr = -1 163 | cdef uint64_t *output_scalar 164 | cdef char* inputStruct 165 | cdef mach_port_t wake_port = 0 166 | cdef uint64_t asyncRef[8] 167 | 168 | # Handle the input scalar first. 169 | if type(scalar_input) is list: 170 | print("++ An input list was passed ++") 171 | 172 | input_scalar_len = len(scalar_input) 173 | print("input scalar len = %d" % input_scalar_len) 174 | 175 | input_scalar_size = input_scalar_len 176 | i = 0 177 | input_scalar = malloc(input_scalar_size * sizeof(uint64_t)) 178 | if not input_scalar: 179 | raise MemoryError() 180 | 181 | # Convert the python array to native. 182 | for elem in scalar_input: 183 | if isinstance(elem, (int, long)): 184 | input_scalar[i] = elem 185 | i += 1 186 | else: 187 | print("++ Scalar input is None or type we cannot convert ++") 188 | input_scalar = NULL 189 | input_scalar_size = 0 190 | 191 | # Now handle the input struct types 192 | if isinstance(structInput, basestring): 193 | print("A string was passed") 194 | #hexstr = ':'.join(x.encode('hex') for x in structInput) 195 | #print(hexstr) 196 | 197 | inputStructCnt = len(structInput) 198 | print("++ Input struct size = " + str(hex(inputStructCnt))) 199 | 200 | # First convert the python string to char * string to get at raw data 201 | inputStruct = structInput 202 | else: 203 | print("++ Struct input is None or type we cannot convert ++") 204 | inputStruct = NULL 205 | inputStructCnt = 0 206 | 207 | # Handle the output scalar here 208 | if type(scalar_output) is list: 209 | print("++ An output list was passed ++") 210 | output_scalar_len = len(scalar_output) 211 | print("output scalar len = %d" % output_scalar_len) 212 | output_scalar_size = output_scalar_len 213 | 214 | output_scalar = malloc(output_scalar_size * sizeof(uint64_t)) 215 | if not output_scalar: 216 | raise MemoryError() 217 | else: 218 | print("++ Scalar output is None or type we cannot convert ++ ") 219 | output_scalar = NULL 220 | output_scalar_size = 0 221 | 222 | # Now handle he output struct. 223 | if isinstance(struct_output, basestring): 224 | print("++ An output string has been passed ++") 225 | outputStructCnt = len(struct_output) 226 | print("Output struct cnt = " + str(outputStructCnt)) 227 | 228 | # Convert our byte string to char * string to get at raw data. 229 | outputStruct = struct_output 230 | else: 231 | print("++ Struct output is None or type we cannot convert ++") 232 | outputStruct = NULL 233 | outputStructCnt = 0 234 | 235 | # Finally make the call 236 | print("%d,%d,%d,%d,%d" % (selector,input_scalar_size,inputStructCnt,output_scalar_size,outputStructCnt)) 237 | kr = IOConnectCallAsyncMethod(conn,selector,wake_port,asyncRef,8,input_scalar,input_scalar_size,inputStruct,inputStructCnt,output_scalar,&output_scalar_size,outputStruct,&outputStructCnt) 238 | 239 | # Scalar output 240 | if output_scalar_size: 241 | print("++ Converting output scalar ++") 242 | 243 | # Struct output 244 | if outputStructCnt: 245 | pass 246 | 247 | # Cleanup our temp buffers. 248 | if input_scalar: 249 | free(input_scalar) 250 | 251 | if output_scalar: 252 | free(output_scalar) 253 | 254 | return kr 255 | 256 | 257 | 258 | def get_port(self,service): 259 | cdef mach_port_t port 260 | if service == "TASK_BOOTSTRAP_PORT": 261 | return bootstrap_port 262 | else: 263 | print("++ Looking up bootstrap service for " + str(service)) 264 | bootstrap_look_up(bootstrap_port, service, &port); 265 | print("++ Found " + str(port)) 266 | return port 267 | 268 | def map_sharedmemory(self,connect,memoryType,size): 269 | print("++ Mapping shared memory ++") 270 | cdef task_port_t intoTask = mach_task_self(); 271 | cdef mach_vm_size_t ofSize = 0; 272 | cdef mach_vm_address_t atAddress = 0; 273 | cdef kern_return_t kr = -1 274 | 275 | ofSize = size; 276 | 277 | kr = IOConnectMapMemory(connect,memoryType,intoTask,&atAddress,&ofSize,0x00000001); 278 | print("IOConnectMapMemory ret = %d" % kr) 279 | print("atAddress = 0x%x" % atAddress) 280 | 281 | if (atAddress != 0): 282 | print("++ mapping ok 0x%x ++" % atAddress) 283 | # Removed as there are read-only mappings 284 | # memset(atAddress,0,ofSize); 285 | else: 286 | print("++ mapping failed ++") 287 | 288 | return atAddress 289 | 290 | def map_unmapmemory(self,connect,memoryType,address): 291 | print("++ Unmapping shared memory ++") 292 | cdef task_port_t fromTask = mach_task_self(); 293 | cdef mach_vm_address_t atAddress; 294 | 295 | atAddress = address; 296 | 297 | kr = IOConnectUnmapMemory(connect,memoryType,fromTask,atAddress); 298 | 299 | return kr 300 | 301 | def is_memory_writable(self,address): 302 | cdef vm_size_t vmsize = 0; 303 | cdef vm_address_t addr; 304 | cdef vm_region_basic_info_data_t info; 305 | cdef mach_msg_type_number_t info_count; 306 | cdef memory_object_name_t object; 307 | cdef kern_return_t status; 308 | cdef unsigned char isWritable = 0; 309 | cdef vm_region_basic_info info_struct; 310 | 311 | info_count = 9 312 | addr = address; 313 | 314 | memset(&info,0,sizeof(info)); 315 | memset(&info_struct,0,sizeof(info_struct)); 316 | 317 | print("addr = 0x%x" % addr) 318 | print("vmsize = %d" % vmsize) 319 | print("info_count = %d" % info_count) 320 | status = vm_region_64(mach_task_self(),&addr,&vmsize,9,&info,&info_count,&object); 321 | print("status = %d" % status) 322 | 323 | info_struct = info; 324 | 325 | if (status == 0): 326 | print("protection = %d" % info_struct.protection) 327 | print("max protection = %d" % info_struct.max_protection) 328 | print("user_wired_count = %d" % info_struct.user_wired_count) 329 | 330 | # VM_PROT_WRITE 331 | isWritable = info_struct.protection & 0x02; 332 | 333 | if (isWritable): 334 | print("++ pointer is writable ++") 335 | return True 336 | else: 337 | print("++ pointer is read-only ++") 338 | return False 339 | else: 340 | print("++ memory is not writable ++") 341 | return False 342 | 343 | def set_sharedmemory(self,address,data,size=4096): 344 | print("++ Setting shared memory at 0x%x ++" % address) 345 | cdef char* inputbuf 346 | cdef mach_vm_address_t atAddress; 347 | inputbuf = data 348 | atAddress = address; 349 | 350 | print("++ Setting shared memory size %d ++" % size) 351 | memcpy(atAddress,inputbuf,size); 352 | 353 | def dump_sharedmemory(self,address,size): 354 | print("++ Dumping shared memory at 0x%x of size %d ++" % (address,size)) 355 | cdef unsigned char *tempbuf 356 | 357 | cdef mach_vm_address_t atAddress; 358 | atAddress = address; 359 | 360 | tempbuf = malloc(size); 361 | memcpy(tempbuf,atAddress,size); 362 | 363 | pystring = "" 364 | 365 | for i in range(size): 366 | pystring += chr(tempbuf[i]) 367 | 368 | #print(pystring) 369 | 370 | return pystring 371 | 372 | def send_mach_msg(self,service,data): 373 | print("mach_msg called") 374 | cdef mach_msg_return_t ret 375 | cdef mach_msg_header_t* msg 376 | cdef char* inputdata 377 | inputdata = data 378 | 379 | # Cast our data to message header struct 380 | msg = inputdata 381 | 382 | # Update the local and remote ports to ones within our process. 383 | msg.msgh_local_port = 0 384 | msg.msgh_remote_port = self.get_port(service) 385 | msg.msgh_bits = 0x131513 386 | 387 | kr = mach_msg_send(msg) 388 | 389 | return kr 390 | 391 | 392 | def ioconnect_addclient(self,f,t): 393 | cdef mach_msg_return_t ret 394 | ret = IOConnectAddClient(f, t); 395 | return ret 396 | 397 | def ioconnect_trap0(self,conn,i): 398 | cdef mach_msg_return_t ret 399 | ret = IOConnectTrap0(conn,i); 400 | return ret 401 | 402 | def ioconnect_trap1(self,conn,i,p): 403 | cdef mach_msg_return_t ret 404 | ret = IOConnectTrap1(conn,i,p); 405 | return ret 406 | 407 | def ioconnect_trap2(self,conn,i,p1,p2): 408 | cdef mach_msg_return_t ret 409 | ret = IOConnectTrap2(conn,i,p1,p2); 410 | return ret 411 | 412 | def ioconnect_trap3(self,conn,i,p1,p2,p3): 413 | cdef mach_msg_return_t ret 414 | ret = IOConnectTrap3(conn,i,p1,p2,p3); 415 | return ret 416 | 417 | def ioconnect_trap4(self,conn,i,p1,p2,p3,p4): 418 | cdef mach_msg_return_t ret 419 | ret = IOConnectTrap4(conn,i,p1,p2,p3,p4); 420 | return ret 421 | 422 | def ioconnect_trap5(self,conn,i,p1,p2,p3,p4,p5): 423 | cdef mach_msg_return_t ret 424 | ret = IOConnectTrap5(conn,i,p1,p2,p3,p4,p5); 425 | return ret 426 | 427 | def ioconnect_trap6(self,conn,i,p1,p2,p3,p4,p5,p6): 428 | cdef mach_msg_return_t ret 429 | ret = IOConnectTrap6(conn,i,p1,p2,p3,p4,p5,p6); 430 | return ret 431 | 432 | def ioconnect_setnotificationport(self,conn,type,port): 433 | cdef uintptr_t reference = 0; 434 | ret = IOConnectSetNotificationPort(conn,type,port,reference); 435 | return ret; 436 | 437 | def ioconnect_getservice(self,conn): 438 | cdef io_service_t service; 439 | cdef mach_msg_return_t ret; 440 | ret = IOConnectGetService(conn, &service); 441 | return service 442 | 443 | def ioconnect_setproperty(self,conn,key,value): 444 | cdef CFStringRef cf_key 445 | cdef CFTypeRef cf_value 446 | 447 | cf_key = CFStringCreateWithCString(NULL,key,0) 448 | cf_value = CFStringCreateWithCString(NULL,value,0) 449 | 450 | kr = IOConnectSetCFProperty(conn,cf_key,cf_value) 451 | 452 | if cf_key: 453 | CFRelease(cf_key) 454 | 455 | if cf_value: 456 | CFRelease(cf_value) 457 | 458 | return kr 459 | 460 | def ioregistry_setproperty(self,service_name,key,value): 461 | cdef kern_return_t kr = -1 462 | cdef io_iterator_t iterator = 0 463 | cdef io_connect_t conn = 0 464 | cdef io_service_t svc = 0 465 | cdef CFDictionaryRef matching 466 | cdef CFStringRef cf_key 467 | cdef CFTypeRef cf_value 468 | 469 | matching = IOServiceMatching(service_name) 470 | svc = IOServiceGetMatchingService(0,matching) 471 | 472 | print("SVC " + str(svc)) 473 | 474 | if (svc == 0): 475 | print("++ IOServiceGetMatchingService could not find service for %s" % service_name) 476 | matching = IOServiceNameMatching(service_name) 477 | svc = IOServiceGetMatchingService(0, matching); 478 | 479 | if (svc == 0): 480 | # TODO: Try to loop for a partial match 481 | print("++ Looking for a partial match ++") 482 | matching = IOServiceMatching("IOService") 483 | IOServiceGetMatchingServices(0, matching, &iterator) 484 | svc = IOIteratorNext(iterator); 485 | 486 | cf_key = CFStringCreateWithCString(NULL,key,0) 487 | cf_value = CFStringCreateWithCString(NULL,value,0) 488 | 489 | kr = IORegistryEntrySetCFProperty(svc,cf_key,cf_value) 490 | 491 | if cf_key: 492 | CFRelease(cf_key) 493 | 494 | if cf_value: 495 | CFRelease(cf_value) 496 | 497 | return kr 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | from Cython.Build import cythonize 4 | 5 | import os 6 | 7 | extra_link_args = ['-framework', 'CoreFoundation', '-framework', 'IOKit'] 8 | 9 | setup( 10 | ext_modules = cythonize([Extension("iokitlib", ["iokitlib.pyx"],extra_link_args = extra_link_args)]) 11 | ) 12 | --------------------------------------------------------------------------------