起因是前几天在Github上刷到了这个 https://github.com/amineross/showcase 仓库。大概意思是找一个旧平板,停掉系统BTStack还有Wifi自己接管。然后就能和iPhone建立iAP2连接,最后协商连接手机Wifi,就能拿到Carplay画面。

这让我非常震惊,因为在 上一篇文章 里面,我写过关于MFi改定位的一小个想法,但是由于MFi改定位会需要手机和真实MFi chip进行交互认证,而这个iPhone可以直接不用MFi chip里的证书就通过认证?

于是我找到了MFi的specification:

4.1 Accessory Authentication Requirements

可以看到这里五步:

  1. RequestAuthenticationCertificate:请求证书(iPhone → MFi)0xAA00
  2. AuthenticationCertificate:发送证书(MFi → iPhone) 0xAA01
  3. RequestAuthenticationChallengeResponse :请求challenge,这里会发送一个nonce交给MFi进行签名(iPhone → MFi)0XAA02
  4. AuthenticationResponse:返回签名完成的challenge(MFi → iPhone)0xAA03
  5. AuthenticationFailed:失败(iPhone → MFi)0xAA04

然后我就去对比了下showcase里的MFi认证的逻辑函数,是这个:

C
static void handle_ctrl_msg(uint16_t msg_id, uint8_t *params, int plen) {    printf("[CTRL] msg=0x%04X (%d bytes)\n", msg_id, plen);    switch (msg_id) {    /* ── Authentication ── */    case 0xAA00:        printf("[CP] *** RequestAuthCert ***\n");        send_auth_cert();        break;    case 0xAA02: {        printf("[CP] *** RequestAuthChallenge ***\n");        if (plen>=4) {            int dlen=((params[0]<<8)|params[1])-4;            if (dlen>0&&dlen<=1024) send_auth_response(&params[4],dlen);        }        break;    }    case 0xAA04: printf("[CP] *** AUTH FAILED ***\n"); break;    case 0xAA05:        mfi_auth_succeeded = 1;        printf("[CP] *** AUTH SUCCEEDED! ***\n");        maybe_start_handoff_watchdog();        break;....

然后是证书申请:

Objective-c
static void issue_baa_cert(void) {    printf("[BAA] Issuing certificate...\n");    void *di = dlopen("/System/Library/PrivateFrameworks/DeviceIdentity.framework/DeviceIdentity", RTLD_NOW);    if (!di) di = dlopen("/System/Library/PrivateFrameworks/MobileActivation.framework/MobileActivation", RTLD_NOW);    if (!di) { printf("[BAA] Cannot load framework!\n"); return; }    typedef void (^DIBlock)(id, id, id);    typedef void (*DIFunc)(id, id, DIBlock);    DIFunc fn = (DIFunc)dlsym(di, "DeviceIdentityIssueClientCertificateWithCompletion");    if (!fn) { printf("[BAA] No issuance func!\n"); return; }    dispatch_semaphore_t sem = dispatch_semaphore_create(0);    fn(nil, [NSDictionary dictionary], ^(id k, id c, id e) {        if (e) { NSLog(@"[BAA] Error: %@", e); dispatch_semaphore_signal(sem); return; }        NSArray *ca = (NSArray *)c;        if ([ca count] < 2) { dispatch_semaphore_signal(sem); return; }        baa_private_key = (SecKeyRef)CFBridgingRetain(k);        CFDataRef d1 = SecCertificateCopyData((__bridge SecCertificateRef)ca[0]);        baa_leaf_len = (int)CFDataGetLength(d1);        baa_leaf_der = malloc(baa_leaf_len); memcpy(baa_leaf_der, CFDataGetBytePtr(d1), baa_leaf_len);        CFRelease(d1);        CFDataRef d2 = SecCertificateCopyData((__bridge SecCertificateRef)ca[1]);        baa_inter_len = (int)CFDataGetLength(d2);        baa_inter_der = malloc(baa_inter_len); memcpy(baa_inter_der, CFDataGetBytePtr(d2), baa_inter_len);        CFRelease(d2);        baa_ready = 1;        printf("[BAA] Leaf=%d Inter=%d — ready\n", baa_leaf_len, baa_inter_len);        dispatch_semaphore_signal(sem);    });    dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, 30LL * NSEC_PER_SEC));}

这里是用的DeviceIdentity/MobileActivation的私有框架完成的。诶你他娘的还真是个天才

这样居然能过iPhone的证书信任链,那AnyGo这个东西我就再也不用付费了嘿嘿嘿。

接下来简单实验了下发现,一般adhoc的二进制过不了amfi,过了之后函数内还有其他的白名单限制。逆向发现这三个进程名字都可以过

  • CarPlay Simulator
  • CarDisplaySim
  • Carplay Sim

于是我们可以简易写一个小注入去绕一下AnyGo的注册登录逻辑,然后把他们的服务端模拟一下,就可以正常修改定位了。并且这个修改在大部分情况下不会被过滤:

过滤的绝大部分是isSimulatedBySoftware,但是这里用的是isProducedByAccessory

然后是关于Location Information的Specs:同样是手机侧先请求,后续再发送位置信息。

非常不错啊,但是我懒得处理细节了,于是就有了这个~

AnyGo

What’s More

同时我们可以注意到苹果自带的Additional Tool里有一个Carplay Simulator:它的连接/认证方式是usb。也就是,理论上这个MFi连接是可以从手机到浏览器的,那么接下来下一步就是把做成有线修改Accessory定位了。

检查之后发现这里Carplay Simulator用的是lockdownd的com.apple.carkit.service 并且在这上面封装了iAP2流。