├── 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 | 
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 |
--------------------------------------------------------------------------------