/* sorbo '07 * * $Id: frontline.c,v 1.5 2007-07-11 20:15:59 root Exp $ */ #include #include #include #include #include #include #include #include #include #define __packed __attribute__((__packed__)) #define STATUS_HDR_ERROR (1 << 2) #define STATUS_CRC_ERROR ((1 << 1) | STATUS_HDR_ERROR) #define STATUS_UNSUPPORTED ((1 << 3) | 1) #define VER_BC2 0xE #define VER_BC4 0xF #define TYPE_DV 8 #define LMP_IN_RAND 8 #define LMP_COMB_KEY 9 #define LMP_AU_RAND 11 #define LMP_SRES 12 #define FRAG_FIRST (1 << 6) #define FRAG_LAST (1 << 7) #define CHAN_DEBUG 20 #define FILTER_DATA 1 #define FILTER_SCO (1 << 1) #define FILTER_NULL_POLL (1 << 2) #define LLID_FRAG 1 #define LLID_START (1 << 1) #define LLID_LMP (LLID_START|LLID_FRAG) #define CMD_START 0x30 #define CMD_STOP 0x32 #define CMD_FILTER 0x33 #define CMD_TIMER 0x34 struct dbg_packet { uint8_t dp_type; uint16_t dp_unknown1; uint16_t dp_unknown2; uint8_t dp_data[19]; } __packed; struct start_packet { uint8_t sp_master_rev[6]; uint32_t sp_unknown; uint8_t sp_slave_rev[6]; } __packed; #define LMP_TID_MASK 1 #define LMP_OP1_SHIFT 1 #define FP_CLOCK_MASK 0xFFFFFFF #define FP_SLAVE_MASK 0x2 #define FP_STATUS_SHIFT 28 #define FP_TYPE_SHIFT 3 #define FP_TYPE_MASK 0xF #define FP_ADDR_MASK 7 #define FP_LEN_LLID_SHIFT 2 #define FP_LEN_LLID_MASK 3 #define FP_LEN_ARQN_MASK 1 #define FP_LEN_SEQN_MASK (1 << 1) #define FP_LEN_FLOW (1 << 4) #define FP_LEN_SHIFT 5 struct frontline_packet { uint8_t fp_ver; uint32_t fp_clock; uint8_t fp_hdr0; uint16_t fp_len; uint32_t fp_timer; uint8_t fp_chan; uint8_t fp_seq; } __packed; struct frontline_packet_bc4 { struct frontline_packet fp_fp; uint8_t fp_decrypted; } __packed; #define MAX_TYPES 16 struct state { int s_fd; int s_buf[1024]; int s_len; int s_llid; int s_master; int s_ignore[MAX_TYPES]; int s_dump; int s_ignore_zero; int s_type; uint8_t s_pin; uint8_t s_pin_data[7][16]; int s_pin_master; } _state; struct hcidump_hdr { uint16_t len; uint8_t in; uint8_t pad; uint32_t ts_sec; uint32_t ts_usec; } __attribute__ ((packed)); static struct state *get_state(void) { return &_state; } static void send_debug(struct state *s, struct dbg_packet *dp, void *rp, int rplen) { unsigned char cp[254]; struct hci_request rq; unsigned char *p = cp; memset(&rq, 0, sizeof(rq)); memset(cp, 0, sizeof(cp)); /* payload descriptor */ *p++ = FRAG_FIRST | FRAG_LAST | CHAN_DEBUG; memcpy(p, dp, sizeof(*dp)); p += sizeof(*dp); rq.ogf = OGF_VENDOR_CMD; rq.ocf = 0x00; rq.event = EVT_VENDOR; rq.cparam = cp; rq.clen = p - cp; rq.rparam = rp; rq.rlen = rplen; if (hci_send_req(s->s_fd, &rq, 2000) < 0) err(1, "hci_send_req()"); } static void send_debug_no_rp(struct state *s, struct dbg_packet *dp) { unsigned char rp[254]; send_debug(s, dp, rp, sizeof(rp)); } static unsigned int get_timer(struct state *s) { unsigned char rp[254]; struct dbg_packet pkt; memset(rp, 0, sizeof(rp)); memset(&pkt, 0, sizeof(pkt)); pkt.dp_type = CMD_TIMER; send_debug(s, &pkt, rp, sizeof(rp)); return *((unsigned int*) &rp[2]); } static void set_filter(struct state *s, unsigned char val) { struct dbg_packet pkt; memset(&pkt, 0, sizeof(pkt)); pkt.dp_type = CMD_FILTER; pkt.dp_data[0] = val; send_debug_no_rp(s, &pkt); } static void sniff_stop(struct state *s) { struct dbg_packet pkt; memset(&pkt, 0, sizeof(pkt)); pkt.dp_type = CMD_STOP; send_debug_no_rp(s, &pkt); } static void sniff_start(struct state *s, unsigned char *master, unsigned char *slave) { struct dbg_packet pkt; struct start_packet *sp = (struct start_packet*) &pkt.dp_data; int i; memset(&pkt, 0, sizeof(pkt)); pkt.dp_type = CMD_START; for (i = 5; i >= 0; i--) sp->sp_master_rev[i] = *master++; for (i = 5; i >= 0; i--) sp->sp_slave_rev[i] = *slave++; send_debug_no_rp(s, &pkt); } static void usage(char *p) { printf( "Usage: %s \n" "-h\thelp\n" "-d\t\n" "-t\ttimer\n" "-f\t\n" "-s\tstop\n" "-S\t\n" "-e\tsniff\n" "-i\t\n" "-z\tignore zero legnth packets\n" "-p\town pin\n" , p); exit(1); } static void str2mac(unsigned char* dst, char* mac) { unsigned int macf[6]; int i; if( sscanf(mac, "%x:%x:%x:%x:%x:%x", &macf[0], &macf[1], &macf[2], &macf[3], &macf[4], &macf[5]) != 6) { printf("can't parse mac %s\n", mac); exit(1); } for (i = 0; i < 6; i++) *dst++ = (unsigned char) macf[i]; } static void parse_macs(char *str, unsigned char *master, unsigned char *slave) { char *div; div = strchr(str, '@'); if (!div) errx(1, "bad macs"); *div++ = 0; str2mac(master, str); str2mac(slave, div); } static void hexdump(void *buf, int len) { unsigned char *p = buf; while (len--) printf("%.2X ", *p++); printf("\n"); } static void process_l2cap(struct state *s, void *buf, int len) { struct hcidump_hdr dh; uint8_t type = HCI_ACLDATA_PKT; hci_acl_hdr acl; int totlen = sizeof(type) + sizeof(acl) + len; printf("L2CAP: "); hexdump(buf, len); if (s->s_dump == -1) return; memset(&dh, 0, sizeof(dh)); dh.len = totlen; dh.in = 1; dh.ts_sec = 0; dh.ts_usec = 0; if (write(s->s_dump, &dh, sizeof(dh)) != sizeof(dh)) err(1, "write()"); if (write(s->s_dump, &type, sizeof(type)) != sizeof(type)) err(1, "write()"); memset(&acl, 0, sizeof(acl)); acl.dlen = len; acl.handle = acl_handle_pack(0, s->s_llid); if (write(s->s_dump, &acl, sizeof(acl)) != sizeof(acl)) err(1, "write()"); if (write(s->s_dump, buf, len) != len) err(1, "write()"); } #define GOT_IN_RAND (1 << 1) #define GOT_COMB1 (1 << 2) #define GOT_COMB2 (1 << 3) #define GOT_AU_RAND1 (1 << 4) #define GOT_SRES1 (1 << 5) #define GOT_AU_RAND2 (1 << 6) #define GOT_SRES2 (1 << 7) static void do_pin(struct state *s, int op, void *buf, int len) { int i, j; switch (op) { case LMP_IN_RAND: s->s_pin = 1 | GOT_IN_RAND; s->s_pin_master = s->s_master; memcpy(s->s_pin_data[0], buf, len); break; case LMP_COMB_KEY: if (!(s->s_pin & GOT_IN_RAND)) return; if (s->s_master == s->s_pin_master) { memcpy(s->s_pin_data[1], buf, len); s->s_pin |= GOT_COMB1; } else { memcpy(s->s_pin_data[2], buf, len); s->s_pin |= GOT_COMB2; } break; case LMP_AU_RAND: if ((!(s->s_pin & GOT_COMB1)) || (!(s->s_pin & GOT_COMB2))) return; if (s->s_master == s->s_pin_master) { memcpy(s->s_pin_data[3], buf, len); s->s_pin |= GOT_AU_RAND1; } else { memcpy(s->s_pin_data[4], buf, len); s->s_pin |= GOT_AU_RAND2; } break; case LMP_SRES: if (s->s_master != s->s_pin_master) { if (!(s->s_pin & GOT_AU_RAND1)) return; memcpy(s->s_pin_data[6], buf, len); s->s_pin |= GOT_SRES1; } else { if (!(s->s_pin & GOT_AU_RAND2)) return; memcpy(s->s_pin_data[5], buf, len); s->s_pin |= GOT_SRES2; } break; default: return; } if (s->s_pin != 0xFF) return; printf("btpincrack Go "); if (s->s_pin_master) printf(" "); else printf(" "); for (i = 0; i < 7; i++) { int len = i >= 5 ? 4 : 16; for (j = 0; j < len; j++) printf("%.2x", s->s_pin_data[i][j]); printf(" "); } printf("\n"); s->s_pin = 1; } static void process_lmp(struct state *s, void *buf, int len) { uint8_t *data = buf; int op1, op2 = -1; int tid; op1 = *data++; len--; assert(len >= 0); tid = op1 & LMP_TID_MASK; op1 >>= LMP_OP1_SHIFT; if (op1 >= 124 && op1 <= 127) { op2 = *data++; len--; assert(len >= 0); } printf("LMP Tid %d Op1 %d", tid, op1); if (op2 != -1) printf(" Op2 %d", op2); printf(": "); hexdump(data, len); if (s->s_pin) do_pin(s, op1, data, len); } static void process_dv(struct state *s, void *buf, int len) { printf("DV: "); hexdump(buf, len); } static void process_payload(struct state *s, void *buf, int len) { switch (s->s_type) { case TYPE_DV: process_dv(s, buf, len); return; } if (s->s_llid == LLID_LMP) process_lmp(s, buf, len); else process_l2cap(s, buf, len); } static void process_frontline(struct state *s, void *buf, int len) { struct frontline_packet *fp = buf; int type = (fp->fp_hdr0 >> FP_TYPE_SHIFT) & FP_TYPE_MASK; int plen = fp->fp_len >> FP_LEN_SHIFT; uint8_t *start = (uint8_t*) fp; int status = fp->fp_hdr0 & FP_ADDR_MASK; int i; int hlen; switch (fp->fp_ver) { case VER_BC2: hlen = sizeof(struct frontline_packet); break; case VER_BC4: hlen = sizeof(struct frontline_packet_bc4); break; default: printf("Unknown ver 0x%.2X\n", fp->fp_ver); abort(); break; } start += hlen; for (i = 0; i < MAX_TYPES; i++) { if (s->s_ignore[i] == type) return; /* XXX check for appended packets */ } if (s->s_ignore_zero && plen == 0) return; s->s_llid = (fp->fp_len >> FP_LEN_LLID_SHIFT) & FP_LEN_LLID_MASK; s->s_master = !(fp->fp_clock & FP_SLAVE_MASK); s->s_type = type; printf("V 0x%.2X Ch %.2d %c Clk 0x%.7X Status 0x%.1X Hdr0 0x%.2X" " [type: %d addr: %d] LLID %d Len %d", fp->fp_ver, fp->fp_chan, s->s_master ? 'M' : 'S', fp->fp_clock & FP_CLOCK_MASK, fp->fp_clock >> FP_STATUS_SHIFT, fp->fp_hdr0, type, status, s->s_llid, plen); len -= hlen; assert(len >= 0); assert(len >= plen); if (plen) { printf(" "); process_payload(s, start, plen); } else printf("\n"); /* firmware seems to append fragments */ len -= plen; assert(len >= 0); if (len) process_frontline(s, start+plen, len); } static void process(struct state *s, void *buf, int len) { uint8_t *type = buf; hci_acl_hdr *acl; if (*type != HCI_ACLDATA_PKT) { printf("Unknown type: %d\n", *type); return; } acl = (hci_acl_hdr*) (type+1); assert(acl->dlen == (len - sizeof(*acl) - 1)); process_frontline(s, acl+1, acl->dlen); } static void sniff(struct state *s) { struct hci_filter flt; hci_filter_clear(&flt); hci_filter_all_ptypes(&flt); hci_filter_all_events(&flt); if (setsockopt(s->s_fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) err(1, "Can't set filter - setsockopt()"); while (1) { s->s_len = read(s->s_fd, s->s_buf, sizeof(s->s_buf)); if (s->s_len == -1) err(1, "read()"); process(s, s->s_buf, s->s_len); } } int main(int argc, char *argv[]) { int dev; int ch; char *device = NULL; int timer = 0; int filter = 0, flt = 0; char *start = NULL; int stop = 0; int snif = 0; struct state *s; int i; char *dump = NULL; s = get_state(); memset(s, 0, sizeof(*s)); for (i = 0; i < MAX_TYPES; i++) s->s_ignore[i] = -1; s->s_dump = -1; while ((ch = getopt(argc, argv, "hd:tf:sS:ei:w:zp")) != -1) { switch (ch) { case 'z': s->s_ignore_zero = 1; break; case 'd': device = optarg; break; case 't': timer = 1; break; case 'f': filter = 1; flt = atoi(optarg); break; case 's': stop = 1; break; case 'S': start = optarg; break; case 'i': for (i = 0; i < MAX_TYPES; i++) { int type = atoi(optarg); if (s->s_ignore[i] == -1 || s->s_ignore[i] == type) { s->s_ignore[i] = type; break; } } break; case 'e': snif = 1; break; case 'w': dump = optarg; break; case 'p': s->s_pin = 1; break; case 'h': default: usage(argv[0]); } } if (dump) { s->s_dump = open(dump, O_APPEND | O_WRONLY | O_CREAT, 0644); if (s->s_dump == -1) err(1, "dump file - open()"); } if (!device) errx(1, "Specify device"); /* open */ if ((dev = hci_devid(device)) < 0) err(1, "hci_devid()"); if ((s->s_fd = hci_open_dev(dev)) < 0) err(1, "hci_devid()"); /* do stuff */ if (timer) printf("Timer %x\n", get_timer(s)); if (filter) set_filter(s, flt); if (stop) sniff_stop(s); if (start) { unsigned char slave[6], master[6]; parse_macs(start, master, slave); sniff_start(s, master, slave); } if (snif) sniff(s); hci_close_dev(s->s_fd); if (s->s_dump != -1) close(s->s_dump); exit(0); }