├── README.jpg ├── Blesniffer.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── project.pbxproj ├── Blesniffer ├── UsbDeviceInterface.h ├── PcapDumpFile.h ├── UsbDeviceManager.h ├── CC2540.h ├── UsbDevice.h ├── CC2540Record.h ├── UsbDevicePipe.h ├── PcapDumpFile.m ├── UsbDeviceManager.m ├── UsbDeviceInterface.m ├── CC2540Record.m ├── UsbDevicePipe.m ├── main.m ├── UsbDevice.m └── CC2540.m ├── LICENSE ├── README.md └── .gitignore /README.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ura14h/Blesniffer/HEAD/README.jpg -------------------------------------------------------------------------------- /Blesniffer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Blesniffer/UsbDeviceInterface.h: -------------------------------------------------------------------------------- 1 | // 2 | // UsbDeviceInterface.h 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/03. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class UsbDevicePipe; 12 | 13 | 14 | @interface UsbDeviceInterface : NSObject 15 | 16 | - (BOOL)open; 17 | - (BOOL)close; 18 | - (NSArray *)pipeList; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Blesniffer/PcapDumpFile.h: -------------------------------------------------------------------------------- 1 | // 2 | // PcapDumpFile.h 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/11. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class CC2540CapturedRecord; 12 | 13 | 14 | @interface PcapDumpFile : NSObject 15 | 16 | - (BOOL)open:(NSString *)path; 17 | - (BOOL)write: (CC2540CapturedRecord *)record; 18 | - (BOOL)close; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Blesniffer/UsbDeviceManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // UsbDeviceManager.h 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/03. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class UsbDevice; 12 | 13 | 14 | @interface UsbDeviceManager : NSObject 15 | 16 | - (BOOL)open; 17 | - (BOOL)close; 18 | - (NSArray *)deviceListWithVendorId: (NSInteger)vendorId productId:(NSInteger)producutId; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Blesniffer/CC2540.h: -------------------------------------------------------------------------------- 1 | // 2 | // CC2540.h 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/10. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class UsbDevice; 12 | @class CC2540Record; 13 | 14 | 15 | @interface CC2540 : NSObject 16 | 17 | + (NSInteger)vendorId; 18 | + (NSInteger)productId; 19 | 20 | - (CC2540 *)initWithUsbDevice: (UsbDevice *)device; 21 | - (BOOL)open; 22 | - (BOOL)close; 23 | - (BOOL)start: (NSInteger)channel; 24 | - (BOOL)stop; 25 | - (CC2540Record *)read; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Blesniffer/UsbDevice.h: -------------------------------------------------------------------------------- 1 | // 2 | // UsbDevice.h 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/03. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class UsbDeviceInterface; 12 | 13 | 14 | @interface UsbDevice : NSObject 15 | 16 | @property (strong) NSString *name; 17 | @property (strong) NSString *path; 18 | 19 | - (BOOL)open; 20 | - (BOOL)close; 21 | - (NSInteger)getConfiguration; 22 | - (BOOL)setConfiguration:(NSInteger)value; 23 | - (NSArray *)interfaceList; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Blesniffer/CC2540Record.h: -------------------------------------------------------------------------------- 1 | // 2 | // CC2540Record.h 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/11. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface CC2540Record : NSObject 13 | 14 | + (instancetype)cc2540recordWithBytes:(void *)bytes length:(NSInteger)length; 15 | 16 | @end 17 | 18 | // MARK: - 19 | 20 | @interface CC2540CapturedRecord : CC2540Record 21 | 22 | @property (assign) struct timeval packetTimestamp; 23 | @property (assign) uint32 packetLength; 24 | @property (assign) void *packetBytes; 25 | @property (assign) int packetRssi; 26 | @property (assign) int packetChannel; 27 | @property (assign) int packetStatus; 28 | @property (assign) int packetPduType; 29 | 30 | @end 31 | 32 | // MARK: - 33 | 34 | @interface CC2540UnknownRecord : CC2540Record 35 | 36 | // No extended implementations. 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Blesniffer/UsbDevicePipe.h: -------------------------------------------------------------------------------- 1 | // 2 | // UsbDevicePipe.h 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/04. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | typedef NS_ENUM(NSUInteger, UsbDevicePipeType) { 13 | UsbDevicePipeTypeControl, 14 | UsbDevicePipeTypeInterrupt, 15 | UsbDevicePipeTypeBulk, 16 | UsbDevicePipeTypeIsochronous, 17 | UsbDevicePipeTypeAny, 18 | UsbDevicePipeTypeUnknown, 19 | }; 20 | 21 | typedef NS_ENUM(NSUInteger, UsbDevicePipeDirection) { 22 | UsbDevicePipeDirectionIn, 23 | UsbDevicePipeDirectionOut, 24 | UsbDevicePipeDirectionUnknown, 25 | }; 26 | 27 | 28 | @interface UsbDevicePipe : NSObject 29 | 30 | @property (assign) UsbDevicePipeType type; 31 | @property (assign) UsbDevicePipeDirection direction; 32 | @property (assign) NSInteger maxPacketSize; 33 | 34 | - (BOOL)clear; 35 | - (BOOL)controlRequest:(IOUSBDevRequest *)request; 36 | - (NSInteger)read:(void *)buffer length:(NSInteger)length; 37 | - (NSInteger)write:(void *)buffer length:(NSInteger)length; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Hiroki Ishiura 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bluetooth LE sniffer 2 | 3 | A Bluetooth LE sniffer for CC2540 USB dongle and Mac. 4 | 5 | ![CC2540 USB dongle](README.jpg) 6 | 7 | This application captures Bluetooth LE packet in air and saves them to a file. 8 | 9 | ## Usage 10 | 11 | This is a command line utility. 12 | 13 | ``` 14 | $ Blesniffer [-c channel#] [-d device#] [-v] output.pcap 15 | ``` 16 | 17 | Press Control + C to stop capturing packets. 18 | 19 | ### Parameters 20 | 21 | * `-c channel#`: RF channel. The channel must be from 0 to 39. One of 37, 38 or 39 is recommended. This is option parameter. The default channel is 37. 22 | * `-d device#`: An index of device in list. This is option parameter. Th default index is 0. 23 | * `-v`: Enter verbose mode. This is option parameter. The default is no-verbose mode. 24 | * `output.pcap`: A name of output file which format is PCAP. If you specified '-', the program outputs captured packets to standard out. 25 | 26 | The content of `output.pcap` can be seen per packet using [Wireshark](https://www.wireshark.org/). 27 | 28 | ## Requirements 29 | 30 | * Texas Instruments [CC2540EMK-USB](http://www.ti.com/tool/cc2540emk-usb) 31 | * macOS 10.12 32 | * Xcode 8.1 33 | 34 | ## License 35 | 36 | Please read [this file](LICENSE). 37 | 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/Preview.html 54 | fastlane/screenshots 55 | fastlane/test_output 56 | 57 | # Code Injection 58 | # 59 | # After new code Injection tools there's a generated folder /iOSInjectionProject 60 | # https://github.com/johnno1962/injectionforxcode 61 | 62 | iOSInjectionProject/ 63 | -------------------------------------------------------------------------------- /Blesniffer/PcapDumpFile.m: -------------------------------------------------------------------------------- 1 | // 2 | // PcapDumpFile.m 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/11. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PcapDumpFile.h" 11 | #import "CC2540Record.h" 12 | 13 | 14 | @interface PcapDumpFile () 15 | 16 | @property (assign) pcap_t *handle; 17 | @property (assign) pcap_dumper_t *dumper; 18 | 19 | @end 20 | 21 | @implementation PcapDumpFile 22 | 23 | - (instancetype)init { 24 | self = [super init]; 25 | if (!self) { 26 | return nil; 27 | } 28 | 29 | _handle = nil; 30 | _dumper = nil; 31 | 32 | return self; 33 | } 34 | 35 | - (void)dealloc { 36 | [self close]; 37 | 38 | _handle = nil; 39 | _dumper = nil; 40 | } 41 | 42 | - (BOOL)open:(NSString *)path { 43 | const int maxPacketSize = 4096; 44 | 45 | pcap_t *handle = pcap_open_dead(DLT_BLUETOOTH_LE_LL_WITH_PHDR, maxPacketSize); 46 | if (!handle) { 47 | return NO; 48 | } 49 | pcap_dumper_t *dumper = pcap_dump_open(handle, [path cStringUsingEncoding:NSUTF8StringEncoding]); 50 | if (!dumper) { 51 | pcap_close(handle); 52 | return NO; 53 | } 54 | 55 | self.handle = handle; 56 | self.dumper = dumper; 57 | 58 | return YES; 59 | } 60 | 61 | - (BOOL)write: (CC2540CapturedRecord *)record { 62 | struct pcap_pkthdr header; 63 | header.caplen = record.packetLength + 10; 64 | header.len = record.packetLength + 10; 65 | header.ts = record.packetTimestamp; 66 | 67 | uint8 body[record.packetLength + 10]; 68 | memset(body, 0x00, 10); 69 | body[0] = record.packetChannel; 70 | body[1] = record.packetRssi; 71 | body[8] = 0x03; // LE Packet is de-whitened, Signal Power field is valid. 72 | body[9] = 0x00; 73 | memcpy(body + 10, record.packetBytes, record.packetLength); 74 | 75 | pcap_dump((u_char *)self.dumper, &header, body); 76 | pcap_dump_flush(self.dumper); 77 | 78 | return YES; 79 | } 80 | 81 | - (BOOL)close { 82 | if (self.dumper) { 83 | pcap_dump_flush(self.dumper); 84 | pcap_dump_close(self.dumper); 85 | } 86 | if (self.handle) { 87 | pcap_close(self.handle); 88 | } 89 | self.dumper = nil; 90 | self.handle = nil; 91 | 92 | return YES; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /Blesniffer/UsbDeviceManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // UsbDeviceManager.m 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/03. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import "UsbDeviceManager.h" 13 | #import "UsbDevice.h" 14 | 15 | @interface UsbDevice (UsbDeviceManager) 16 | 17 | - (instancetype)initWithManager:(UsbDeviceManager *)manager service:(io_service_t)service; 18 | 19 | @end 20 | 21 | 22 | @interface UsbDeviceManager () 23 | 24 | @property (assign) mach_port_t masterPort; 25 | 26 | @end 27 | 28 | @implementation UsbDeviceManager 29 | 30 | - (instancetype)init { 31 | self = [super init]; 32 | if (!self) { 33 | return nil; 34 | } 35 | 36 | _masterPort = 0; 37 | 38 | return self; 39 | } 40 | 41 | - (void)dealloc { 42 | [self close]; 43 | } 44 | 45 | - (BOOL)open { 46 | if (self.masterPort != 0) { 47 | return YES; 48 | } 49 | 50 | kern_return_t result = IOMasterPort(MACH_PORT_NULL, &_masterPort); 51 | if (result != KERN_SUCCESS) { 52 | return NO; 53 | } 54 | 55 | return YES; 56 | } 57 | 58 | - (BOOL)close { 59 | if (self.masterPort == 0) { 60 | return YES; 61 | } 62 | 63 | kern_return_t result = mach_port_deallocate(mach_host_self(), self.masterPort); 64 | if (result != KERN_SUCCESS) { 65 | return NO; 66 | } 67 | self.masterPort = 0; 68 | 69 | return YES; 70 | } 71 | 72 | - (NSArray *)deviceListWithVendorId: (NSInteger)vendorId productId:(NSInteger)producutId { 73 | if (self.masterPort == 0) { 74 | return nil; 75 | } 76 | 77 | NSMutableDictionary *matching = (__bridge NSMutableDictionary *)IOServiceMatching(kIOUSBDeviceClassName); 78 | matching[@kUSBVendorID] = @(vendorId); 79 | matching[@kUSBProductID] = @(producutId); 80 | 81 | io_iterator_t iterator = 0; 82 | kern_return_t result = IOServiceGetMatchingServices(self.masterPort, (__bridge CFDictionaryRef)matching, &iterator); 83 | if (result != KERN_SUCCESS) { 84 | return nil; 85 | } 86 | 87 | NSMutableArray *deviceList = [NSMutableArray array]; 88 | io_service_t service = 0; 89 | while ((service = IOIteratorNext(iterator)) != 0) { 90 | UsbDevice *device = [[UsbDevice alloc] initWithManager:self service:service]; 91 | if (device) { 92 | [deviceList addObject:device]; 93 | } 94 | IOObjectRelease(service); 95 | } 96 | IOObjectRelease(iterator); 97 | 98 | return deviceList; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /Blesniffer/UsbDeviceInterface.m: -------------------------------------------------------------------------------- 1 | // 2 | // UsbDeviceInterface.m 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/03. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "UsbDeviceManager.h" 12 | #import "UsbDevice.h" 13 | #import "UsbDeviceInterface.h" 14 | #import "UsbDevicePipe.h" 15 | 16 | @interface UsbDevicePipe (UsbDeviceInterface) 17 | 18 | - (instancetype)initWithInterface:(UsbDeviceInterface *)interface index:(NSUInteger)index; 19 | 20 | @end 21 | 22 | @interface UsbDeviceInterface () 23 | 24 | @property (strong) UsbDevice *device; 25 | @property (assign) io_service_t service; 26 | @property (assign) IOUSBInterfaceInterface245 **interfaceInterface; 27 | 28 | @end 29 | 30 | @implementation UsbDeviceInterface 31 | 32 | - (instancetype)initWithDevice:(UsbDevice *)device service:(io_service_t)service { 33 | self = [super init]; 34 | if (!self) { 35 | return nil; 36 | } 37 | 38 | _device = device; 39 | _service = service; 40 | IOObjectRetain(_service); 41 | 42 | _interfaceInterface = nil; 43 | 44 | return self; 45 | } 46 | 47 | - (void)dealloc { 48 | [self close]; 49 | 50 | if (self.service) { 51 | IOObjectRelease(self.service); 52 | } 53 | self.service = 0; 54 | self.device = nil; 55 | } 56 | 57 | - (BOOL)open { 58 | if (self.interfaceInterface) { 59 | return YES; 60 | } 61 | 62 | IOReturn result; 63 | 64 | IOCFPlugInInterface **pluginInterface = nil; 65 | SInt32 score = 0; 66 | result = IOCreatePlugInInterfaceForService(self.service, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &pluginInterface, &score); 67 | if (result != KERN_SUCCESS || pluginInterface == nil) { 68 | return NO; 69 | } 70 | 71 | IOUSBInterfaceInterface245 **interfaceInterface = nil; 72 | result = (*pluginInterface)->QueryInterface(pluginInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID245), (LPVOID)&interfaceInterface); 73 | IODestroyPlugInInterface(pluginInterface); 74 | if (result != KERN_SUCCESS || interfaceInterface == nil) { 75 | return NO; 76 | } 77 | 78 | result = (*interfaceInterface)->USBInterfaceOpen(interfaceInterface); 79 | if (result != KERN_SUCCESS) { 80 | (*interfaceInterface)->Release(interfaceInterface); 81 | return NO; 82 | } 83 | 84 | self.interfaceInterface = interfaceInterface; 85 | return YES; 86 | } 87 | 88 | - (BOOL)close { 89 | if (self.interfaceInterface) { 90 | (*(self.interfaceInterface))->USBInterfaceClose(self.interfaceInterface); 91 | (*(self.interfaceInterface))->Release(self.interfaceInterface); 92 | } 93 | 94 | self.interfaceInterface = nil; 95 | return YES; 96 | } 97 | 98 | - (NSArray *)pipeList { 99 | if (!self.interfaceInterface) { 100 | return nil; 101 | } 102 | 103 | IOUSBInterfaceInterface245 **interfaceInterface = self.interfaceInterface; 104 | UInt8 pipes = 0; 105 | IOReturn result = (*interfaceInterface)->GetNumEndpoints(interfaceInterface, &pipes); 106 | if (result != KERN_SUCCESS) { 107 | return NO; 108 | } 109 | 110 | NSMutableArray *pipeList = [NSMutableArray array]; 111 | for (UInt8 index = 0; index <= pipes; index++) { 112 | UsbDevicePipe *pipe = [[UsbDevicePipe alloc] initWithInterface:self index:(NSUInteger)index]; 113 | if (pipe) { 114 | [pipeList addObject:pipe]; 115 | } 116 | } 117 | 118 | return pipeList; 119 | } 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /Blesniffer/CC2540Record.m: -------------------------------------------------------------------------------- 1 | // 2 | // CC2540Record.m 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/11. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import "CC2540Record.h" 10 | 11 | 12 | // MARK: - 13 | 14 | @interface CC2540Record () 15 | 16 | - (instancetype)initWithBytes:(void *)bytes length:(NSInteger)length; 17 | 18 | @end 19 | 20 | @implementation CC2540Record 21 | 22 | + (instancetype)cc2540recordWithBytes:(void *)bytes length:(NSInteger)length { 23 | CC2540Record *record = [[CC2540CapturedRecord alloc] initWithBytes:bytes length:length]; 24 | if (!record) { 25 | record = [[CC2540UnknownRecord alloc] initWithBytes:bytes length:length]; 26 | } 27 | return record; 28 | } 29 | 30 | - (instancetype)initWithBytes:(void *)bytes length:(NSInteger)length { 31 | self = [super init]; 32 | if (!self) { 33 | return nil; 34 | } 35 | 36 | return self; 37 | } 38 | 39 | @end 40 | 41 | // MARK: - 42 | 43 | struct CC2540CapturedRecordHeader { 44 | uint8 type; // 0x00 is capture data. 45 | uint16 length; // Hmmm... I don't use this field because its value may be corrupted. 46 | uint32 timestamp; 47 | uint8 preamble[1]; // BLE preamble? 48 | uint8 packet[0]; // BLE address? 49 | } __attribute__((packed)); 50 | 51 | struct CC2540CapturedRecordFooter { 52 | uint8 rssi; // it contains RSSI ? 53 | uint8 flags; // it contains that this frame is valid or invalid ? 54 | } __attribute__((packed)); 55 | 56 | const size_t HeaderLength = sizeof(struct CC2540CapturedRecordHeader); 57 | const size_t FooterLength = sizeof(struct CC2540CapturedRecordFooter); 58 | const size_t MinimumLength = HeaderLength + FooterLength; 59 | 60 | 61 | @implementation CC2540CapturedRecord 62 | 63 | - (instancetype)initWithBytes:(void *)bytes length:(NSInteger)length { 64 | if (![CC2540CapturedRecord validateBytes:bytes length:length]) { 65 | return nil; 66 | } 67 | 68 | self = [super initWithBytes:bytes length:length]; 69 | if (!self) { 70 | return nil; 71 | } 72 | 73 | [self parseBytes:bytes length:length]; 74 | 75 | return self; 76 | } 77 | 78 | - (void)dealloc { 79 | if (self.packetBytes) { 80 | free(self.packetBytes); 81 | self.packetBytes = nil; 82 | } 83 | } 84 | 85 | + (BOOL)validateBytes:(uint8 *)bytes length:(NSInteger)length { 86 | if (length < MinimumLength) { 87 | return NO; 88 | } 89 | if (*bytes != 0x00) { 90 | return NO; 91 | } 92 | 93 | return YES; 94 | } 95 | 96 | - (void)parseBytes:(uint8 *)bytes length:(NSInteger)length { 97 | struct CC2540CapturedRecordHeader *header = (struct CC2540CapturedRecordHeader *)bytes; 98 | 99 | // Hmm... Is this correct? 100 | const time_t nanoSeconds = 1000000000; 101 | const time_t microSeconds = 1000000; 102 | const time_t nanoToMicro = nanoSeconds / microSeconds; 103 | struct timeval packetTimestamp; 104 | packetTimestamp.tv_sec = (time_t)header->timestamp / nanoSeconds; 105 | packetTimestamp.tv_usec = (header->timestamp % nanoSeconds) / nanoToMicro; 106 | 107 | uint32 packetLength = (uint32)((size_t)length - MinimumLength); 108 | void *packetBytes = malloc(packetLength); 109 | memcpy(packetBytes, header->packet, packetLength); 110 | 111 | struct CC2540CapturedRecordFooter *footer = (struct CC2540CapturedRecordFooter *)(bytes + length - FooterLength); 112 | int packetRssi = (char)footer->rssi; 113 | int packetChannel = footer->flags & 0x7f; 114 | int packetStatus = (footer->flags & 0x80 ? 1 : 0); 115 | 116 | int packetPduType = 0; 117 | if (packetLength > 5) { 118 | packetPduType = ((uint8 *)packetBytes)[5] >> 4; 119 | } 120 | 121 | self.packetTimestamp = packetTimestamp; 122 | self.packetLength = packetLength; 123 | self.packetBytes = packetBytes; 124 | self.packetRssi = packetRssi; 125 | self.packetChannel = packetChannel; 126 | self.packetStatus = packetStatus; 127 | self.packetPduType = packetPduType; 128 | } 129 | 130 | @end 131 | 132 | // MARK: - 133 | 134 | @implementation CC2540UnknownRecord 135 | 136 | // No implementations. 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /Blesniffer/UsbDevicePipe.m: -------------------------------------------------------------------------------- 1 | // 2 | // UsbDevicePipe.m 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/04. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "UsbDeviceManager.h" 12 | #import "UsbDevice.h" 13 | #import "UsbDeviceInterface.h" 14 | #import "UsbDevicePipe.h" 15 | 16 | @interface UsbDeviceInterface (UsbDevicePipe) 17 | 18 | @property (assign) IOUSBInterfaceInterface245 **interfaceInterface; 19 | 20 | @end 21 | 22 | @interface UsbDevicePipe () 23 | 24 | @property (assign) UsbDeviceInterface *interface; 25 | @property (assign) NSUInteger index; 26 | 27 | @end 28 | 29 | @implementation UsbDevicePipe 30 | 31 | - (instancetype)initWithInterface:(UsbDeviceInterface *)interface index:(NSUInteger)index { 32 | self = [super init]; 33 | if (!self) { 34 | return nil; 35 | } 36 | 37 | _interface = interface; 38 | _index = index; 39 | 40 | IOUSBInterfaceInterface245 **interfaceInterface = interface.interfaceInterface; 41 | UInt8 direction = 0; 42 | UInt8 number = 0; 43 | UInt8 transferType = 0; 44 | UInt16 maxPacketSize = 0; 45 | UInt8 interval = 0; 46 | IOReturn result = (*interfaceInterface)->GetPipeProperties(interfaceInterface, _index, &direction, &number, &transferType, &maxPacketSize, &interval); 47 | if (result != KERN_SUCCESS) { 48 | return nil; 49 | } 50 | 51 | switch (direction) { 52 | case kUSBOut: 53 | _direction = UsbDevicePipeDirectionOut; 54 | break; 55 | case kUSBIn: 56 | _direction = UsbDevicePipeDirectionIn; 57 | break; 58 | default: 59 | _direction = UsbDevicePipeDirectionUnknown; 60 | break; 61 | } 62 | switch (transferType) { 63 | case kUSBControl: 64 | _type = UsbDevicePipeTypeControl; 65 | break; 66 | case kUSBIsoc: 67 | _type = UsbDevicePipeTypeIsochronous; 68 | break; 69 | case kUSBBulk: 70 | _type = UsbDevicePipeTypeBulk; 71 | break; 72 | case kUSBInterrupt: 73 | _type = UsbDevicePipeTypeInterrupt; 74 | break; 75 | case kUSBAnyType: 76 | _type = UsbDevicePipeTypeAny; 77 | break; 78 | default: 79 | _type = UsbDevicePipeTypeUnknown; 80 | break; 81 | } 82 | _maxPacketSize = (NSInteger)maxPacketSize; 83 | 84 | return self; 85 | } 86 | 87 | - (void)dealloc { 88 | self.interface = nil; 89 | } 90 | 91 | - (BOOL)clear { 92 | 93 | IOUSBInterfaceInterface245 **interfaceInterface = self.interface.interfaceInterface; 94 | IOReturn result = (*interfaceInterface)->ClearPipeStall(interfaceInterface, self.index); 95 | if (result != KERN_SUCCESS) { 96 | return NO; 97 | } 98 | 99 | return YES; 100 | } 101 | 102 | - (BOOL)controlRequest:(IOUSBDevRequest *)request { 103 | if (self.type != UsbDevicePipeTypeControl) { 104 | return NO; 105 | } 106 | 107 | IOUSBInterfaceInterface245 **interfaceInterface = self.interface.interfaceInterface; 108 | IOReturn result = (*interfaceInterface)->ControlRequest(interfaceInterface, self.index, request); 109 | if (result != KERN_SUCCESS) { 110 | return NO; 111 | } 112 | 113 | return YES; 114 | } 115 | 116 | - (NSInteger)read:(void *)buffer length:(NSInteger)length { 117 | if (self.type != UsbDevicePipeTypeBulk && self.type != UsbDevicePipeTypeInterrupt) { 118 | return -1; 119 | } 120 | if (self.direction != UsbDevicePipeDirectionIn) { 121 | return -1; 122 | } 123 | 124 | IOUSBInterfaceInterface245 **interfaceInterface = self.interface.interfaceInterface; 125 | UInt32 readLength = (UInt32)length; 126 | IOReturn result = (*interfaceInterface)->ReadPipe(interfaceInterface, self.index, buffer, &readLength); 127 | if (result != KERN_SUCCESS) { 128 | return -1; 129 | } 130 | 131 | return (NSInteger)readLength; 132 | } 133 | 134 | - (NSInteger)write:(void *)buffer length:(NSInteger)length { 135 | if (self.type != UsbDevicePipeTypeBulk && self.type != UsbDevicePipeTypeInterrupt) { 136 | return -1; 137 | } 138 | if (self.direction != UsbDevicePipeDirectionOut) { 139 | return -1; 140 | } 141 | 142 | IOUSBInterfaceInterface245 **interfaceInterface = self.interface.interfaceInterface; 143 | UInt32 writeLength = (UInt32)length; 144 | IOReturn result = (*interfaceInterface)->WritePipe(interfaceInterface, self.index, buffer, writeLength); 145 | if (result != KERN_SUCCESS) { 146 | return -1; 147 | } 148 | 149 | return (NSInteger)writeLength; 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /Blesniffer/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/03. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "UsbDeviceManager.h" 12 | #import "UsbDevice.h" 13 | #import "CC2540.h" 14 | #import "CC2540Record.h" 15 | #import "PcapDumpFile.h" 16 | 17 | static volatile const char *ApplicationVersion __attribute__((unused)) = "1.0.1"; 18 | 19 | static volatile BOOL VerboseMode = NO; 20 | static volatile BOOL ReadingRecord = YES; 21 | 22 | static void verbose(const char *format, ...) { 23 | if (!VerboseMode) { 24 | return; 25 | } 26 | 27 | va_list args; 28 | va_start(args, format); 29 | vfprintf(stderr, format, args); 30 | va_end(args); 31 | fflush(stderr); 32 | } 33 | 34 | static void signalHandler(int signal) { 35 | ReadingRecord = NO; 36 | } 37 | 38 | int main(int argc, const char *argv[]) { 39 | @autoreleasepool { 40 | const char *argv0 = argv[0]; 41 | 42 | int channelNumber = 0; 43 | int deviceNumber = 0; 44 | { 45 | int optch; 46 | extern char *optarg; 47 | extern int optind; 48 | extern int opterr; 49 | while ((optch = getopt(argc, (char **)argv, "c:d:v")) != -1) { 50 | switch (optch) { 51 | case 'c': 52 | channelNumber = atoi(optarg); 53 | if (channelNumber < 0 || channelNumber > 39) { 54 | fprintf(stderr, "%s: Channel number is out of range.\n", argv0); 55 | exit(1); 56 | } 57 | break; 58 | case 'd': 59 | deviceNumber = atoi(optarg); 60 | break; 61 | case 'v': 62 | VerboseMode = YES; 63 | break; 64 | default: 65 | exit(1); 66 | break; 67 | } 68 | } 69 | argc -= optind; 70 | argv += optind; 71 | } 72 | 73 | if (argc < 1) { 74 | NSString *applicationPath = [NSString stringWithCString:argv0 encoding:NSUTF8StringEncoding]; 75 | NSString *applicationFile = [applicationPath lastPathComponent]; 76 | const char *applicationName = [applicationFile UTF8String]; 77 | 78 | fprintf(stderr, "Usage: %s [-c channel#] [-d device#] [-v] output.pcap\n", applicationName); 79 | fprintf(stderr, " (!) control-c makes exiting packet capturing.\n"); 80 | exit(1); 81 | } 82 | NSString *output = [NSString stringWithCString:argv[0] encoding:NSUTF8StringEncoding]; 83 | if ([output isEqualToString:@"-"]) { 84 | verbose("output is stdout.\n"); 85 | } else { 86 | if (![[output lowercaseString] hasSuffix:@".pcap"]) { 87 | output = [NSString stringWithFormat:@"%@.pcap", output]; 88 | } 89 | } 90 | const char *outputFile = [output UTF8String]; 91 | 92 | 93 | UsbDeviceManager *manager = [UsbDeviceManager new]; 94 | if (![manager open]) { 95 | fprintf(stderr, "%s: Could not open USB device manager.\n", argv0); 96 | exit(1); 97 | } 98 | 99 | NSInteger vendorId = [CC2540 vendorId]; 100 | NSInteger productId = [CC2540 productId]; 101 | NSArray *deviceList = [manager deviceListWithVendorId:vendorId productId:productId]; 102 | if (deviceList.count < 1) { 103 | fprintf(stderr, "%s: No CC2540 USB dongles.\n", argv0); 104 | exit(1); 105 | } 106 | 107 | if (deviceNumber < 0 || deviceNumber >= deviceList.count) { 108 | fprintf(stderr, "%s: Device number is out of range.\n", argv0); 109 | exit(1); 110 | } 111 | 112 | UsbDevice *device = deviceList[deviceNumber]; 113 | verbose("device: %s\n", [device.path UTF8String]); 114 | CC2540 *cc2540 = [[CC2540 alloc] initWithUsbDevice:device]; 115 | if (![cc2540 open]) { 116 | fprintf(stderr, "%s: Could not open CC2540 USB dongle.\n", argv0); 117 | exit(1); 118 | } 119 | 120 | NSString *filename = [NSString stringWithCString:outputFile encoding:NSUTF8StringEncoding]; 121 | PcapDumpFile *file = [[PcapDumpFile alloc] init]; 122 | if (![file open:filename]) { 123 | fprintf(stderr, "%s: Could not open output.\n", argv0); 124 | exit(1); 125 | } 126 | if (![cc2540 start: channelNumber]) { 127 | fprintf(stderr, "%s: Could not start capturing packet.\n", argv0); 128 | exit(1); 129 | } 130 | 131 | verbose("start to capture.\n"); 132 | signal(SIGINT, signalHandler); 133 | NSUInteger number = 0; 134 | while (ReadingRecord) { 135 | @autoreleasepool { 136 | CC2540Record *record = [cc2540 read]; 137 | if (!record) { 138 | if (ReadingRecord) { 139 | fprintf(stderr, "%s: Could not read data.\n", argv0); 140 | } else { 141 | verbose("\n"); 142 | } 143 | break; 144 | } 145 | if ([record isKindOfClass:[CC2540CapturedRecord class]]) { 146 | CC2540CapturedRecord *capturedRecord = (CC2540CapturedRecord *)record; 147 | verbose("%c", (capturedRecord.packetPduType > 0) ? 148 | ((char)(capturedRecord.packetPduType) + '0') : '?'); 149 | [file write:capturedRecord]; 150 | } 151 | } 152 | number++; 153 | } 154 | verbose("stop capturing.\n"); 155 | 156 | [cc2540 stop]; 157 | [file close]; 158 | [cc2540 close]; 159 | [manager close]; 160 | 161 | } 162 | 163 | exit(0); 164 | } 165 | -------------------------------------------------------------------------------- /Blesniffer/UsbDevice.m: -------------------------------------------------------------------------------- 1 | // 2 | // UsbDevice.m 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/03. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import "UsbDeviceManager.h" 14 | #import "UsbDeviceInterface.h" 15 | #import "UsbDevice.h" 16 | 17 | @interface UsbDeviceInterface (UsbDevice) 18 | 19 | - (instancetype)initWithDevice:(UsbDevice *)device service:(io_service_t)service; 20 | 21 | @end 22 | 23 | @interface UsbDevice () 24 | 25 | @property (strong) UsbDeviceManager *manager; 26 | @property (assign) io_service_t service; 27 | @property (assign) IOUSBDeviceInterface245 **deviceInterface; 28 | 29 | @end 30 | 31 | @implementation UsbDevice 32 | 33 | - (instancetype)initWithManager:(UsbDeviceManager *)manager service:(io_service_t)service { 34 | self = [super init]; 35 | if (!self) { 36 | return nil; 37 | } 38 | 39 | _manager = manager; 40 | _service = service; 41 | IOObjectRetain(_service); 42 | 43 | _name = nil; 44 | { 45 | io_name_t name; 46 | kern_return_t result = IORegistryEntryGetName(_service, name); 47 | if (result == KERN_SUCCESS) { 48 | _name = [NSString stringWithCString:name encoding:NSASCIIStringEncoding]; 49 | } 50 | } 51 | _path = nil; 52 | { 53 | io_string_t path; 54 | kern_return_t result = IORegistryEntryGetPath(_service, kIOUSBPlane, path); 55 | if (result == KERN_SUCCESS) { 56 | _path = [NSString stringWithCString:path encoding:NSASCIIStringEncoding]; 57 | } 58 | } 59 | _deviceInterface = nil; 60 | 61 | return self; 62 | } 63 | 64 | - (void)dealloc { 65 | [self close]; 66 | 67 | self.name = nil; 68 | self.path = nil; 69 | 70 | if (self.service) { 71 | IOObjectRelease(self.service); 72 | } 73 | self.service = 0; 74 | self.manager = nil; 75 | } 76 | 77 | - (BOOL)open { 78 | if (self.deviceInterface) { 79 | return YES; 80 | } 81 | 82 | IOReturn result; 83 | 84 | IOCFPlugInInterface **pluginInterface = nil; 85 | SInt32 score = 0; 86 | result = IOCreatePlugInInterfaceForService(self.service, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &pluginInterface, &score); 87 | if (result != KERN_SUCCESS || pluginInterface == nil) { 88 | return NO; 89 | } 90 | 91 | IOUSBDeviceInterface245 **deviceInterface = nil; 92 | result = (*pluginInterface)->QueryInterface(pluginInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245), (LPVOID)&deviceInterface); 93 | IODestroyPlugInInterface(pluginInterface); 94 | if (result != KERN_SUCCESS || deviceInterface == nil) { 95 | return NO; 96 | } 97 | 98 | result = (*deviceInterface)->USBDeviceOpen(deviceInterface); 99 | if (result != KERN_SUCCESS) { 100 | (*deviceInterface)->Release(deviceInterface); 101 | return NO; 102 | } 103 | 104 | self.deviceInterface = deviceInterface; 105 | return YES; 106 | } 107 | 108 | - (BOOL)close { 109 | if (self.deviceInterface) { 110 | (*(self.deviceInterface))->USBDeviceClose(self.deviceInterface); 111 | (*(self.deviceInterface))->Release(self.deviceInterface); 112 | } 113 | 114 | self.deviceInterface = nil; 115 | return YES; 116 | } 117 | 118 | - (NSInteger)numberOfConfigurations { 119 | if (!self.deviceInterface) { 120 | return NO; 121 | } 122 | IOUSBDeviceInterface245 **deviceInterface = self.deviceInterface; 123 | IOReturn result; 124 | 125 | UInt8 configurations = 0; 126 | 127 | result = (*deviceInterface)->GetNumberOfConfigurations(deviceInterface, &configurations); 128 | if (result != KERN_SUCCESS || configurations == 0) { 129 | return -1; 130 | } 131 | IOUSBConfigurationDescriptorPtr configurationDescriptor = nil; 132 | result = (*deviceInterface)->GetConfigurationDescriptorPtr(deviceInterface, 0, &configurationDescriptor); 133 | if (result != KERN_SUCCESS || configurationDescriptor == nil) { 134 | return -1; 135 | } 136 | 137 | return configurations; 138 | } 139 | 140 | - (NSInteger)getConfiguration { 141 | NSInteger configurations = [self numberOfConfigurations]; 142 | if (configurations < 1) { 143 | return NO; 144 | } 145 | 146 | UInt8 configuration = 0; 147 | 148 | IOUSBDeviceInterface245 **deviceInterface = self.deviceInterface; 149 | IOReturn result = (*deviceInterface)->GetConfiguration(deviceInterface, &configuration); 150 | if (result != KERN_SUCCESS) { 151 | return NO; 152 | } 153 | 154 | return (NSInteger)configuration; 155 | } 156 | 157 | - (BOOL)setConfiguration:(NSInteger)value { 158 | NSInteger configurations = [self numberOfConfigurations]; 159 | if (configurations < 1) { 160 | return NO; 161 | } 162 | 163 | IOUSBDeviceInterface245 **deviceInterface = self.deviceInterface; 164 | IOReturn result = (*deviceInterface)->SetConfiguration(deviceInterface, (UInt8)value); 165 | if (result != KERN_SUCCESS) { 166 | return NO; 167 | } 168 | 169 | return YES; 170 | } 171 | 172 | - (NSArray *)interfaceList { 173 | if (!self.deviceInterface) { 174 | return nil; 175 | } 176 | 177 | IOUSBDeviceInterface245 **deviceInterface = self.deviceInterface; 178 | IOUSBFindInterfaceRequest interfaceRequest = { 179 | .bInterfaceClass = kIOUSBFindInterfaceDontCare, 180 | .bInterfaceSubClass = kIOUSBFindInterfaceDontCare, 181 | .bInterfaceProtocol = kIOUSBFindInterfaceDontCare, 182 | .bAlternateSetting = kIOUSBFindInterfaceDontCare, 183 | }; 184 | io_iterator_t iterator = 0; 185 | IOReturn result = (*deviceInterface)->CreateInterfaceIterator(deviceInterface, &interfaceRequest, &iterator); 186 | if (result != KERN_SUCCESS) { 187 | return nil; 188 | } 189 | 190 | NSMutableArray *interfaceList = [NSMutableArray array]; 191 | io_service_t service = 0; 192 | while ((service = IOIteratorNext(iterator)) != 0) { 193 | UsbDeviceInterface *interface = [[UsbDeviceInterface alloc] initWithDevice:self service:service]; 194 | if (interface) { 195 | [interfaceList addObject:interface]; 196 | } 197 | IOObjectRelease(service); 198 | } 199 | IOObjectRelease(iterator); 200 | 201 | return interfaceList; 202 | } 203 | 204 | @end 205 | -------------------------------------------------------------------------------- /Blesniffer/CC2540.m: -------------------------------------------------------------------------------- 1 | // 2 | // CC2540.m 3 | // Blesniffer 4 | // 5 | // Created by Hiroki Ishiura on 2016/11/10. 6 | // Copyright © 2016年 Hiroki Ishiura. All rights reserved. 7 | // 8 | 9 | #import "UsbDevice.h" 10 | #import "UsbDeviceInterface.h" 11 | #import "UsbDevicePipe.h" 12 | #import "CC2540.h" 13 | #import "CC2540Record.h" 14 | 15 | 16 | @interface CC2540 () 17 | 18 | @property (strong) UsbDevice *device; 19 | @property (strong) UsbDeviceInterface *interface; 20 | @property (strong) UsbDevicePipe *control; 21 | @property (strong) UsbDevicePipe *bulk; 22 | 23 | @end 24 | 25 | @implementation CC2540 26 | 27 | + (NSInteger)vendorId { 28 | return 0x0451; 29 | } 30 | 31 | + (NSInteger)productId { 32 | return 0x16B3; 33 | } 34 | 35 | - (CC2540 *)initWithUsbDevice: (UsbDevice *)device { 36 | self = [super init]; 37 | if (!self) { 38 | return nil; 39 | } 40 | 41 | _device = device; 42 | _interface = nil; 43 | _control = nil; 44 | _bulk = nil; 45 | 46 | return self; 47 | } 48 | 49 | - (void)dealloc { 50 | [self close]; 51 | 52 | _device = nil; 53 | } 54 | 55 | - (BOOL)open { 56 | 57 | if (![self.device open]) { 58 | return NO; 59 | } 60 | 61 | NSInteger configuration = [self.device getConfiguration]; 62 | if (configuration < 0) { 63 | return NO; 64 | } 65 | if (![self.device setConfiguration:configuration]) { 66 | return NO; 67 | } 68 | NSArray *interfaceList = [self.device interfaceList]; 69 | if (interfaceList.count < 1) { 70 | return NO; 71 | } 72 | UsbDeviceInterface *interface = interfaceList[0]; 73 | if (![interface open]) { 74 | return NO; 75 | } 76 | NSArray *pipeList = [interface pipeList]; 77 | if (pipeList.count < 1) { 78 | return NO; 79 | } 80 | NSPredicate *controlPredicate = [NSPredicate predicateWithFormat:@"%K = %d", @"type", UsbDevicePipeTypeControl]; 81 | NSArray *controls = [pipeList filteredArrayUsingPredicate:controlPredicate]; 82 | if (controls.count < 1) { 83 | return NO; 84 | } 85 | UsbDevicePipe *control = controls[0]; 86 | NSPredicate *blukPredicate = [NSPredicate predicateWithFormat:@"%K = %d and %K = %d", @"type", UsbDevicePipeTypeBulk, @"direction", UsbDevicePipeDirectionIn]; 87 | NSArray *bulks = [pipeList filteredArrayUsingPredicate:blukPredicate]; 88 | if (bulks.count < 1) { 89 | return NO; 90 | } 91 | UsbDevicePipe *bulk = bulks[0]; 92 | 93 | self.interface = interface; 94 | self.control = control; 95 | self.bulk = bulk; 96 | 97 | return YES; 98 | } 99 | 100 | - (BOOL)close { 101 | [self stop]; 102 | 103 | self.control = nil; 104 | self.bulk = nil; 105 | 106 | if (self.interface) { 107 | [self.interface close]; 108 | } 109 | 110 | self.interface = nil; 111 | 112 | if (self.device) { 113 | [self.device close]; 114 | } 115 | 116 | return YES; 117 | } 118 | 119 | - (BOOL)start: (NSInteger)channel { 120 | if (channel == 0) { 121 | channel = 37; 122 | } 123 | 124 | { 125 | uint8 data[8] = { 0 }; 126 | IOUSBDevRequest request = { 127 | .bmRequestType = USBmakebmRequestType(kUSBIn, kUSBVendor, kUSBDevice), 128 | .bRequest = 0xc0, 129 | .wValue = 0, 130 | .wIndex = 0, 131 | .wLength = 8, 132 | .pData = data, 133 | .wLenDone = 0, 134 | }; 135 | if (![self.control controlRequest:&request]) { 136 | return NO; 137 | } 138 | } 139 | if (![self.bulk clear]) { 140 | return 1; 141 | } 142 | { 143 | IOUSBDevRequest request = { 144 | .bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice), 145 | .bRequest = 0xc5, 146 | .wValue = 0, 147 | .wIndex = 4, 148 | .wLength = 0, 149 | .pData = nil, 150 | .wLenDone = 0, 151 | }; 152 | if (![self.control controlRequest:&request]) { 153 | return NO; 154 | } 155 | } 156 | uint8 requestC6 = 0; 157 | while (requestC6 != 0x04) { 158 | uint8 data[1] = { 0 }; 159 | IOUSBDevRequest request = { 160 | .bmRequestType = USBmakebmRequestType(kUSBIn, kUSBVendor, kUSBDevice), 161 | .bRequest = 0xc6, 162 | .wValue = 0, 163 | .wIndex = 0, 164 | .wLength = 1, 165 | .pData = data, 166 | .wLenDone = 0, 167 | }; 168 | if (![self.control controlRequest:&request]) { 169 | return NO; 170 | } 171 | requestC6 = data[0]; 172 | } 173 | { 174 | IOUSBDevRequest request = { 175 | .bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice), 176 | .bRequest = 0xc9, 177 | .wValue = 0, 178 | .wIndex = 0, 179 | .wLength = 0, 180 | .pData = nil, 181 | .wLenDone = 0, 182 | }; 183 | if (![self.control controlRequest:&request]) { 184 | return NO; 185 | } 186 | } 187 | { 188 | uint8 data[1] = { (uint8)channel }; 189 | IOUSBDevRequest request = { 190 | .bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice), 191 | .bRequest = 0xd2, 192 | .wValue = 0, 193 | .wIndex = 0, 194 | .wLength = 1, 195 | .pData = data, 196 | .wLenDone = 0, 197 | }; 198 | if (![self.control controlRequest:&request]) { 199 | return NO; 200 | } 201 | } 202 | { 203 | uint8 data[1] = { 0x00 }; 204 | IOUSBDevRequest request = { 205 | .bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice), 206 | .bRequest = 0xd2, 207 | .wValue = 0, 208 | .wIndex = 1, 209 | .wLength = 1, 210 | .pData = data, 211 | .wLenDone = 0, 212 | }; 213 | if (![self.control controlRequest:&request]) { 214 | return NO; 215 | } 216 | } 217 | { 218 | IOUSBDevRequest request = { 219 | .bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice), 220 | .bRequest = 0xd0, 221 | .wValue = 0, 222 | .wIndex = 0, 223 | .wLength = 0, 224 | .pData = nil, 225 | .wLenDone = 0, 226 | }; 227 | if (![self.control controlRequest:&request]) { 228 | return NO; 229 | } 230 | } 231 | 232 | return YES; 233 | } 234 | 235 | - (BOOL)stop { 236 | { 237 | IOUSBDevRequest request = { 238 | .bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice), 239 | .bRequest = 0xd1, 240 | .wValue = 0, 241 | .wIndex = 0, 242 | .wLength = 0, 243 | .pData = nil, 244 | .wLenDone = 0, 245 | }; 246 | if (![self.control controlRequest:&request]) { 247 | return NO; 248 | } 249 | } 250 | { 251 | IOUSBDevRequest request = { 252 | .bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice), 253 | .bRequest = 0xc5, 254 | .wValue = 0, 255 | .wIndex = 0, 256 | .wLength = 0, 257 | .pData = nil, 258 | .wLenDone = 0, 259 | }; 260 | if (![self.control controlRequest:&request]) { 261 | return NO; 262 | } 263 | } 264 | 265 | return YES; 266 | } 267 | 268 | - (CC2540Record *)read { 269 | NSInteger bufferLength = self.bulk.maxPacketSize; 270 | uint8 buffer[bufferLength]; 271 | NSInteger readLength = [self.bulk read:buffer length:bufferLength]; 272 | if (readLength < 0) { 273 | return nil; 274 | } 275 | 276 | CC2540Record *record = [CC2540Record cc2540recordWithBytes:buffer length:readLength]; 277 | return record; 278 | } 279 | 280 | @end 281 | -------------------------------------------------------------------------------- /Blesniffer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3100854F1DCB486800C896F3 /* UsbDeviceManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3100854E1DCB486800C896F3 /* UsbDeviceManager.m */; }; 11 | 310085521DCB559E00C896F3 /* UsbDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 310085511DCB559E00C896F3 /* UsbDevice.m */; }; 12 | 310085551DCB7B1200C896F3 /* UsbDeviceInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 310085541DCB7B1200C896F3 /* UsbDeviceInterface.m */; }; 13 | 3100855B1DCC166600C896F3 /* UsbDevicePipe.m in Sources */ = {isa = PBXBuildFile; fileRef = 3100855A1DCC166600C896F3 /* UsbDevicePipe.m */; }; 14 | 31C344FE1DCB177D00C53E33 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 31C344FD1DCB177D00C53E33 /* main.m */; }; 15 | 31CE6AA51DD5E67300FCC797 /* CC2540Record.m in Sources */ = {isa = PBXBuildFile; fileRef = 31CE6AA41DD5E67300FCC797 /* CC2540Record.m */; }; 16 | 31CE6AA81DD5FDDC00FCC797 /* PcapDumpFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 31CE6AA71DD5FDDC00FCC797 /* PcapDumpFile.m */; }; 17 | 31CE6AAB1DD5FE6F00FCC797 /* libpcap.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 31CE6AAA1DD5FE6F00FCC797 /* libpcap.tbd */; }; 18 | 31E01E371DD48DE200FD8AC0 /* CC2540.m in Sources */ = {isa = PBXBuildFile; fileRef = 31E01E361DD48DE200FD8AC0 /* CC2540.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXCopyFilesBuildPhase section */ 22 | 31C344F81DCB177D00C53E33 /* CopyFiles */ = { 23 | isa = PBXCopyFilesBuildPhase; 24 | buildActionMask = 2147483647; 25 | dstPath = /usr/share/man/man1/; 26 | dstSubfolderSpec = 0; 27 | files = ( 28 | ); 29 | runOnlyForDeploymentPostprocessing = 1; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 3100854D1DCB486800C896F3 /* UsbDeviceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UsbDeviceManager.h; sourceTree = ""; }; 35 | 3100854E1DCB486800C896F3 /* UsbDeviceManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UsbDeviceManager.m; sourceTree = ""; }; 36 | 310085501DCB559E00C896F3 /* UsbDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UsbDevice.h; sourceTree = ""; }; 37 | 310085511DCB559E00C896F3 /* UsbDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UsbDevice.m; sourceTree = ""; }; 38 | 310085531DCB7B1200C896F3 /* UsbDeviceInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UsbDeviceInterface.h; sourceTree = ""; }; 39 | 310085541DCB7B1200C896F3 /* UsbDeviceInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UsbDeviceInterface.m; sourceTree = ""; }; 40 | 310085591DCC166600C896F3 /* UsbDevicePipe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UsbDevicePipe.h; sourceTree = ""; }; 41 | 3100855A1DCC166600C896F3 /* UsbDevicePipe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UsbDevicePipe.m; sourceTree = ""; }; 42 | 31C344FA1DCB177D00C53E33 /* Blesniffer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Blesniffer; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 31C344FD1DCB177D00C53E33 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 44 | 31CE6AA31DD5E67300FCC797 /* CC2540Record.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CC2540Record.h; sourceTree = ""; }; 45 | 31CE6AA41DD5E67300FCC797 /* CC2540Record.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CC2540Record.m; sourceTree = ""; }; 46 | 31CE6AA61DD5FDDC00FCC797 /* PcapDumpFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PcapDumpFile.h; sourceTree = ""; }; 47 | 31CE6AA71DD5FDDC00FCC797 /* PcapDumpFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PcapDumpFile.m; sourceTree = ""; }; 48 | 31CE6AAA1DD5FE6F00FCC797 /* libpcap.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libpcap.tbd; path = usr/lib/libpcap.tbd; sourceTree = SDKROOT; }; 49 | 31E01E351DD48DE200FD8AC0 /* CC2540.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CC2540.h; sourceTree = ""; }; 50 | 31E01E361DD48DE200FD8AC0 /* CC2540.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CC2540.m; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 31C344F71DCB177D00C53E33 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | 31CE6AAB1DD5FE6F00FCC797 /* libpcap.tbd in Frameworks */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXFrameworksBuildPhase section */ 63 | 64 | /* Begin PBXGroup section */ 65 | 31C344F11DCB177D00C53E33 = { 66 | isa = PBXGroup; 67 | children = ( 68 | 31C344FC1DCB177D00C53E33 /* Blesniffer */, 69 | 31C344FB1DCB177D00C53E33 /* Products */, 70 | 31CE6AA91DD5FE6F00FCC797 /* Frameworks */, 71 | ); 72 | sourceTree = ""; 73 | }; 74 | 31C344FB1DCB177D00C53E33 /* Products */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 31C344FA1DCB177D00C53E33 /* Blesniffer */, 78 | ); 79 | name = Products; 80 | sourceTree = ""; 81 | }; 82 | 31C344FC1DCB177D00C53E33 /* Blesniffer */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 31C344FD1DCB177D00C53E33 /* main.m */, 86 | 31E01E351DD48DE200FD8AC0 /* CC2540.h */, 87 | 31E01E361DD48DE200FD8AC0 /* CC2540.m */, 88 | 31CE6AA31DD5E67300FCC797 /* CC2540Record.h */, 89 | 31CE6AA41DD5E67300FCC797 /* CC2540Record.m */, 90 | 31CE6AA61DD5FDDC00FCC797 /* PcapDumpFile.h */, 91 | 31CE6AA71DD5FDDC00FCC797 /* PcapDumpFile.m */, 92 | 31CE6AAC1DD61ABD00FCC797 /* UsbDeviceControl */, 93 | ); 94 | path = Blesniffer; 95 | sourceTree = ""; 96 | }; 97 | 31CE6AA91DD5FE6F00FCC797 /* Frameworks */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 31CE6AAA1DD5FE6F00FCC797 /* libpcap.tbd */, 101 | ); 102 | name = Frameworks; 103 | sourceTree = ""; 104 | }; 105 | 31CE6AAC1DD61ABD00FCC797 /* UsbDeviceControl */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 3100854D1DCB486800C896F3 /* UsbDeviceManager.h */, 109 | 3100854E1DCB486800C896F3 /* UsbDeviceManager.m */, 110 | 310085501DCB559E00C896F3 /* UsbDevice.h */, 111 | 310085511DCB559E00C896F3 /* UsbDevice.m */, 112 | 310085531DCB7B1200C896F3 /* UsbDeviceInterface.h */, 113 | 310085541DCB7B1200C896F3 /* UsbDeviceInterface.m */, 114 | 310085591DCC166600C896F3 /* UsbDevicePipe.h */, 115 | 3100855A1DCC166600C896F3 /* UsbDevicePipe.m */, 116 | ); 117 | name = UsbDeviceControl; 118 | sourceTree = ""; 119 | }; 120 | /* End PBXGroup section */ 121 | 122 | /* Begin PBXNativeTarget section */ 123 | 31C344F91DCB177D00C53E33 /* Blesniffer */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = 31C345011DCB177D00C53E33 /* Build configuration list for PBXNativeTarget "Blesniffer" */; 126 | buildPhases = ( 127 | 31C344F61DCB177D00C53E33 /* Sources */, 128 | 31C344F71DCB177D00C53E33 /* Frameworks */, 129 | 31C344F81DCB177D00C53E33 /* CopyFiles */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = Blesniffer; 136 | productName = Blesniffer; 137 | productReference = 31C344FA1DCB177D00C53E33 /* Blesniffer */; 138 | productType = "com.apple.product-type.tool"; 139 | }; 140 | /* End PBXNativeTarget section */ 141 | 142 | /* Begin PBXProject section */ 143 | 31C344F21DCB177D00C53E33 /* Project object */ = { 144 | isa = PBXProject; 145 | attributes = { 146 | LastUpgradeCheck = 0810; 147 | ORGANIZATIONNAME = "Hiroki Ishiura"; 148 | TargetAttributes = { 149 | 31C344F91DCB177D00C53E33 = { 150 | CreatedOnToolsVersion = 8.1; 151 | DevelopmentTeam = U853Z6LKZW; 152 | ProvisioningStyle = Automatic; 153 | }; 154 | }; 155 | }; 156 | buildConfigurationList = 31C344F51DCB177D00C53E33 /* Build configuration list for PBXProject "Blesniffer" */; 157 | compatibilityVersion = "Xcode 3.2"; 158 | developmentRegion = English; 159 | hasScannedForEncodings = 0; 160 | knownRegions = ( 161 | en, 162 | ); 163 | mainGroup = 31C344F11DCB177D00C53E33; 164 | productRefGroup = 31C344FB1DCB177D00C53E33 /* Products */; 165 | projectDirPath = ""; 166 | projectRoot = ""; 167 | targets = ( 168 | 31C344F91DCB177D00C53E33 /* Blesniffer */, 169 | ); 170 | }; 171 | /* End PBXProject section */ 172 | 173 | /* Begin PBXSourcesBuildPhase section */ 174 | 31C344F61DCB177D00C53E33 /* Sources */ = { 175 | isa = PBXSourcesBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | 31CE6AA51DD5E67300FCC797 /* CC2540Record.m in Sources */, 179 | 31E01E371DD48DE200FD8AC0 /* CC2540.m in Sources */, 180 | 3100855B1DCC166600C896F3 /* UsbDevicePipe.m in Sources */, 181 | 31CE6AA81DD5FDDC00FCC797 /* PcapDumpFile.m in Sources */, 182 | 310085521DCB559E00C896F3 /* UsbDevice.m in Sources */, 183 | 3100854F1DCB486800C896F3 /* UsbDeviceManager.m in Sources */, 184 | 310085551DCB7B1200C896F3 /* UsbDeviceInterface.m in Sources */, 185 | 31C344FE1DCB177D00C53E33 /* main.m in Sources */, 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | }; 189 | /* End PBXSourcesBuildPhase section */ 190 | 191 | /* Begin XCBuildConfiguration section */ 192 | 31C344FF1DCB177D00C53E33 /* Debug */ = { 193 | isa = XCBuildConfiguration; 194 | buildSettings = { 195 | ALWAYS_SEARCH_USER_PATHS = NO; 196 | CLANG_ANALYZER_NONNULL = YES; 197 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 198 | CLANG_CXX_LIBRARY = "libc++"; 199 | CLANG_ENABLE_MODULES = YES; 200 | CLANG_ENABLE_OBJC_ARC = YES; 201 | CLANG_WARN_BOOL_CONVERSION = YES; 202 | CLANG_WARN_CONSTANT_CONVERSION = YES; 203 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 204 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 205 | CLANG_WARN_EMPTY_BODY = YES; 206 | CLANG_WARN_ENUM_CONVERSION = YES; 207 | CLANG_WARN_INFINITE_RECURSION = YES; 208 | CLANG_WARN_INT_CONVERSION = YES; 209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 210 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 211 | CLANG_WARN_UNREACHABLE_CODE = YES; 212 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 213 | CODE_SIGN_IDENTITY = "-"; 214 | COPY_PHASE_STRIP = NO; 215 | DEBUG_INFORMATION_FORMAT = dwarf; 216 | ENABLE_STRICT_OBJC_MSGSEND = YES; 217 | ENABLE_TESTABILITY = YES; 218 | GCC_C_LANGUAGE_STANDARD = gnu99; 219 | GCC_DYNAMIC_NO_PIC = NO; 220 | GCC_NO_COMMON_BLOCKS = YES; 221 | GCC_OPTIMIZATION_LEVEL = 0; 222 | GCC_PREPROCESSOR_DEFINITIONS = ( 223 | "DEBUG=1", 224 | "$(inherited)", 225 | ); 226 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 227 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 228 | GCC_WARN_UNDECLARED_SELECTOR = YES; 229 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 230 | GCC_WARN_UNUSED_FUNCTION = YES; 231 | GCC_WARN_UNUSED_VARIABLE = YES; 232 | MACOSX_DEPLOYMENT_TARGET = 10.12; 233 | MTL_ENABLE_DEBUG_INFO = YES; 234 | ONLY_ACTIVE_ARCH = YES; 235 | SDKROOT = macosx; 236 | }; 237 | name = Debug; 238 | }; 239 | 31C345001DCB177D00C53E33 /* Release */ = { 240 | isa = XCBuildConfiguration; 241 | buildSettings = { 242 | ALWAYS_SEARCH_USER_PATHS = NO; 243 | CLANG_ANALYZER_NONNULL = YES; 244 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 245 | CLANG_CXX_LIBRARY = "libc++"; 246 | CLANG_ENABLE_MODULES = YES; 247 | CLANG_ENABLE_OBJC_ARC = YES; 248 | CLANG_WARN_BOOL_CONVERSION = YES; 249 | CLANG_WARN_CONSTANT_CONVERSION = YES; 250 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 251 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 252 | CLANG_WARN_EMPTY_BODY = YES; 253 | CLANG_WARN_ENUM_CONVERSION = YES; 254 | CLANG_WARN_INFINITE_RECURSION = YES; 255 | CLANG_WARN_INT_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 258 | CLANG_WARN_UNREACHABLE_CODE = YES; 259 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 260 | CODE_SIGN_IDENTITY = "-"; 261 | COPY_PHASE_STRIP = NO; 262 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 263 | ENABLE_NS_ASSERTIONS = NO; 264 | ENABLE_STRICT_OBJC_MSGSEND = YES; 265 | GCC_C_LANGUAGE_STANDARD = gnu99; 266 | GCC_NO_COMMON_BLOCKS = YES; 267 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 268 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 269 | GCC_WARN_UNDECLARED_SELECTOR = YES; 270 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 271 | GCC_WARN_UNUSED_FUNCTION = YES; 272 | GCC_WARN_UNUSED_VARIABLE = YES; 273 | MACOSX_DEPLOYMENT_TARGET = 10.12; 274 | MTL_ENABLE_DEBUG_INFO = NO; 275 | SDKROOT = macosx; 276 | }; 277 | name = Release; 278 | }; 279 | 31C345021DCB177D00C53E33 /* Debug */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | DEVELOPMENT_TEAM = U853Z6LKZW; 283 | PRODUCT_NAME = "$(TARGET_NAME)"; 284 | }; 285 | name = Debug; 286 | }; 287 | 31C345031DCB177D00C53E33 /* Release */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | DEVELOPMENT_TEAM = U853Z6LKZW; 291 | PRODUCT_NAME = "$(TARGET_NAME)"; 292 | }; 293 | name = Release; 294 | }; 295 | /* End XCBuildConfiguration section */ 296 | 297 | /* Begin XCConfigurationList section */ 298 | 31C344F51DCB177D00C53E33 /* Build configuration list for PBXProject "Blesniffer" */ = { 299 | isa = XCConfigurationList; 300 | buildConfigurations = ( 301 | 31C344FF1DCB177D00C53E33 /* Debug */, 302 | 31C345001DCB177D00C53E33 /* Release */, 303 | ); 304 | defaultConfigurationIsVisible = 0; 305 | defaultConfigurationName = Release; 306 | }; 307 | 31C345011DCB177D00C53E33 /* Build configuration list for PBXNativeTarget "Blesniffer" */ = { 308 | isa = XCConfigurationList; 309 | buildConfigurations = ( 310 | 31C345021DCB177D00C53E33 /* Debug */, 311 | 31C345031DCB177D00C53E33 /* Release */, 312 | ); 313 | defaultConfigurationIsVisible = 0; 314 | defaultConfigurationName = Release; 315 | }; 316 | /* End XCConfigurationList section */ 317 | }; 318 | rootObject = 31C344F21DCB177D00C53E33 /* Project object */; 319 | } 320 | --------------------------------------------------------------------------------