Created
July 5, 2026 20:31
-
-
Save JJTech0130/a878496479afedb93dd02399551e3aec to your computer and use it in GitHub Desktop.
Implements very basic test of the iAP2 protocol over "com.apple.carkit.service", the lockdownd service used by CarPlay Simulator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Implements very basic test of the iAP2 protocol over "com.apple.carkit.service", the lockdownd service used by CarPlay Simulator | |
| import asyncio | |
| from pymobiledevice3.lockdown import create_using_usbmux | |
| def checksum(data): | |
| return (0x100 - sum(data)) & 0xFF | |
| def build_packet(control, seq, ack, session_id=0, payload=b''): | |
| length = 9 + (len(payload) + 1 if payload else 0) | |
| header = bytes([0xFF, 0x5A, length >> 8, length & 0xFF, control, seq, ack, session_id]) | |
| header += bytes([checksum(header)]) | |
| return header + payload + bytes([checksum(payload)]) if payload else header | |
| async def recv_packet(service): | |
| header = await service.recvall(9) | |
| length = (header[2] << 8) | header[3] | |
| payload = await service.recvall(length - 9) if length > 9 else b'' | |
| return header + payload | |
| async def iap1_probe(service): | |
| probe = bytes([0xFF, 0x55, 0x02, 0x00, 0xEE, 0x10]) | |
| print(f"[iap1] sending: {probe.hex()}") | |
| await service.sendall(probe) | |
| resp = await service.recvall(6) | |
| print(f"[iap1] received: {resp.hex()}") | |
| return resp | |
| async def iap2_handshake(service): | |
| seq = 100 | |
| # LinkVer, MaxOutstanding, MaxRecvLen(4096), RetransTimeout(2000ms), CumAckTimeout(22ms), | |
| # MaxRetrans(30), MaxCumAcks(3), Session[id=1, type=0 control, version=2 (required for CarPlay)] | |
| sync_payload = bytes([0x01, 0x05, 0x10, 0x00, 0x07, 0xD0, 0x00, 0x16, 0x1E, 0x03, 0x01, 0x00, 0x02]) | |
| syn = build_packet(0x80, seq, 0, payload=sync_payload) | |
| print(f"[iap2] sending SYN: {syn.hex()}") | |
| await service.sendall(syn) | |
| syn_ack = await recv_packet(service) | |
| print(f"[iap2] received: {syn_ack.hex()}") | |
| ack = build_packet(0x40, seq, syn_ack[5]) # final ACK, no payload | |
| print(f"[iap2] sending ACK: {ack.hex()}") | |
| await service.sendall(ack) | |
| return syn_ack | |
| async def connect(): | |
| lockdown = await create_using_usbmux() | |
| service = await lockdown.start_lockdown_service("com.apple.carkit.service") | |
| # Technically you're supposed to send an iAP1 probe first, I think? but it doesn't appear to affect anything | |
| #await iap1_probe(service) | |
| await iap2_handshake(service) | |
| return service | |
| if __name__ == "__main__": | |
| asyncio.run(connect()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment