├── .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 |
--------------------------------------------------------------------------------