├── README.md └── bluecrawl-1.0.0.js /README.md: -------------------------------------------------------------------------------- 1 | # BlueCrawl 2 | 3 | Bluetooth meta-data collector for Android (using Frida JS). BlueCrawl serves to aid any bluetooth based research folks seek to do on android. 4 | As with any other protocol, reverse engineering it requires seeing how it is interpreted from different view points; here I present the viewpoint of the 5 | java layer in the application. Having this info can also aid a variety of activities involving spoofing bluetooth information and checking whether apps are stealing 6 | or maliciously scanning for bluetooth objects. 7 | 8 | ## Example 9 | 10 | ``` 11 | > frida -U -l bluecrawl-1.0.0.js com.android.bluetooth 12 | ____ 13 | / _ | Frida 12.2.15 - A world-class dynamic instrumentation toolkit 14 | | (_| | 15 | > _ | Commands: 16 | /_/ |_| help -> Displays the help system 17 | . . . . object? -> Display information about 'object' 18 | . . . . exit/quit -> Exit 19 | . . . . 20 | . . . . More info at http://www.frida.re/docs/home/ 21 | 22 | [LGE Nexus 5X::com.android.bluetooth]-> 23 | ---------- BlueCrawl : Bluetooth Metadata collector 24 | v1.0.0 25 | 26 | finding loaded bluetooth classes 27 | [->] android.bluetooth.IBluetoothManagerCallback$Stub 28 | [->] android.bluetooth.IBluetoothHeadset$Stub 29 | [->] android.bluetooth.IBluetoothProfileServiceConnection$Stub 30 | [->] android.bluetooth.BluetoothProfile$ServiceListener 31 | [->] android.bluetooth.IBluetoothHeadset 32 | [->] android.bluetooth.BluetoothGattServerCallback 33 | ... 34 | [*] ----- 35 | 36 | searching loaded classes for 'android.bluetooth.BluetoothSocket'... 37 | [*] 'android.bluetooth.BluetoothSocket' instance found 'android.bluetooth.BluetoothSocket@c445cb2' 38 | [*] getting info for 'android.bluetooth.BluetoothSocket@c445cb2' 39 | [*] - isConnected :false 40 | [*] - connection type :(1) L2CAP 41 | [x] - conneted device : device details not available 42 | [*] - InputStream :android.bluetooth.BluetoothInputStream@ceae875 43 | [*] - OutputStream :android.bluetooth.BluetoothOutputStream@256d00a 44 | 45 | [*] 'android.bluetooth.BluetoothSocket' instance found 'android.bluetooth.BluetoothSocket@5f8bd5f' 46 | [*] getting info for 'android.bluetooth.BluetoothSocket@5f8bd5f' 47 | [*] - isConnected :false 48 | [*] - connection type :(3) SCO 49 | [x] - conneted device : device details not available 50 | [*] - InputStream :android.bluetooth.BluetoothInputStream@3b7d3f3 51 | [*] - OutputStream :android.bluetooth.BluetoothOutputStream@8cb78b0 52 | 53 | [*] ----- 54 | 55 | searching loaded classes for 'android.bluetooth.BluetoothServerSocket'... 56 | [*] 'android.bluetooth.BluetoothServerSocket' instance found 'ServerSocket: Type: TYPE_RFCOMM Channel: 5' 57 | [*] 'android.bluetooth.BluetoothServerSocket' instance found 'ServerSocket: Type: TYPE_RFCOMM Channel: 4' 58 | [*] 'android.bluetooth.BluetoothServerSocket' instance found 'ServerSocket: Type: TYPE_L2CAP Channel: 4097' 59 | [*] 'android.bluetooth.BluetoothServerSocket' instance found 'ServerSocket: Type: TYPE_RFCOMM Channel: 6' 60 | [*] 'android.bluetooth.BluetoothServerSocket' instance found 'ServerSocket: Type: TYPE_L2CAP Channel: 4099' 61 | [*] 'android.bluetooth.BluetoothServerSocket' instance found 'ServerSocket: Type: TYPE_L2CAP Channel: 4101' 62 | [*] 'android.bluetooth.BluetoothServerSocket' instance found 'ServerSocket: Type: TYPE_RFCOMM Channel: 7' 63 | [*] ----- 64 | 65 | searching loaded classes for 'android.bluetooth.BluetoothDevice'... 66 | [*] android.bluetooth.BluetoothDevice instance found :=> 'FF:FF:FF:BE:EE:EF' 67 | [*] Bluetooth device ['FF:FF:FF:BE:EE:EF'] 68 | [*] - Name :Nexus 5 69 | [*] - Address :FF:FF:FF:BE:EE:EF 70 | [*] - Type :(1) Classic 71 | [*] - Device Class :5a020c 72 | [*] -- Class Major : (512) Phone 73 | [*] - Bond State :(12) Bonded 74 | ``` 75 | *Disclaimer : MAC addresses shown here are fabricated entirely. 76 | Prints in color too! 77 | -------------------------------------------------------------------------------- /bluecrawl-1.0.0.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | 4 | GNU LESSER GENERAL PUBLIC LICENSE 5 | Version 3, 29 June 2007 6 | 7 | Copyright (C) 2007 Free Software Foundation, Inc. 8 | Everyone is permitted to copy and distribute verbatim copies 9 | of this license document, but changing it is not allowed. 10 | 11 | 12 | This version of the GNU Lesser General Public License incorporates 13 | the terms and conditions of version 3 of the GNU General Public 14 | License, supplemented by the additional permissions listed below. 15 | 16 | 0. Additional Definitions. 17 | 18 | As used herein, "this License" refers to version 3 of the GNU Lesser 19 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 20 | General Public License. 21 | 22 | "The Library" refers to a covered work governed by this License, 23 | other than an Application or a Combined Work as defined below. 24 | 25 | An "Application" is any work that makes use of an interface provided 26 | by the Library, but which is not otherwise based on the Library. 27 | Defining a subclass of a class defined by the Library is deemed a mode 28 | of using an interface provided by the Library. 29 | 30 | A "Combined Work" is a work produced by combining or linking an 31 | Application with the Library. The particular version of the Library 32 | with which the Combined Work was made is also called the "Linked 33 | Version". 34 | 35 | The "Minimal Corresponding Source" for a Combined Work means the 36 | Corresponding Source for the Combined Work, excluding any source code 37 | for portions of the Combined Work that, considered in isolation, are 38 | based on the Application, and not on the Linked Version. 39 | 40 | The "Corresponding Application Code" for a Combined Work means the 41 | object code and/or source code for the Application, including any data 42 | and utility programs needed for reproducing the Combined Work from the 43 | Application, but excluding the System Libraries of the Combined Work. 44 | 45 | 1. Exception to Section 3 of the GNU GPL. 46 | 47 | You may convey a covered work under sections 3 and 4 of this License 48 | without being bound by section 3 of the GNU GPL. 49 | 50 | 2. Conveying Modified Versions. 51 | 52 | If you modify a copy of the Library, and, in your modifications, a 53 | facility refers to a function or data to be supplied by an Application 54 | that uses the facility (other than as an argument passed when the 55 | facility is invoked), then you may convey a copy of the modified 56 | version: 57 | 58 | a) under this License, provided that you make a good faith effort to 59 | ensure that, in the event an Application does not supply the 60 | function or data, the facility still operates, and performs 61 | whatever part of its purpose remains meaningful, or 62 | 63 | b) under the GNU GPL, with none of the additional permissions of 64 | this License applicable to that copy. 65 | 66 | 3. Object Code Incorporating Material from Library Header Files. 67 | 68 | The object code form of an Application may incorporate material from 69 | a header file that is part of the Library. You may convey such object 70 | code under terms of your choice, provided that, if the incorporated 71 | material is not limited to numerical parameters, data structure 72 | layouts and accessors, or small macros, inline functions and templates 73 | (ten or fewer lines in length), you do both of the following: 74 | 75 | a) Give prominent notice with each copy of the object code that the 76 | Library is used in it and that the Library and its use are 77 | covered by this License. 78 | 79 | b) Accompany the object code with a copy of the GNU GPL and this license 80 | document. 81 | 82 | 4. Combined Works. 83 | 84 | You may convey a Combined Work under terms of your choice that, 85 | taken together, effectively do not restrict modification of the 86 | portions of the Library contained in the Combined Work and reverse 87 | engineering for debugging such modifications, if you also do each of 88 | the following: 89 | 90 | a) Give prominent notice with each copy of the Combined Work that 91 | the Library is used in it and that the Library and its use are 92 | covered by this License. 93 | 94 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 95 | document. 96 | 97 | c) For a Combined Work that displays copyright notices during 98 | execution, include the copyright notice for the Library among 99 | these notices, as well as a reference directing the user to the 100 | copies of the GNU GPL and this license document. 101 | 102 | d) Do one of the following: 103 | 104 | 0) Convey the Minimal Corresponding Source under the terms of this 105 | License, and the Corresponding Application Code in a form 106 | suitable for, and under terms that permit, the user to 107 | recombine or relink the Application with a modified version of 108 | the Linked Version to produce a modified Combined Work, in the 109 | manner specified by section 6 of the GNU GPL for conveying 110 | Corresponding Source. 111 | 112 | 1) Use a suitable shared library mechanism for linking with the 113 | Library. A suitable mechanism is one that (a) uses at run time 114 | a copy of the Library already present on the user's computer 115 | system, and (b) will operate properly with a modified version 116 | of the Library that is interface-compatible with the Linked 117 | Version. 118 | 119 | e) Provide Installation Information, but only if you would otherwise 120 | be required to provide such information under section 6 of the 121 | GNU GPL, and only to the extent that such information is 122 | necessary to install and execute a modified version of the 123 | Combined Work produced by recombining or relinking the 124 | Application with a modified version of the Linked Version. (If 125 | you use option 4d0, the Installation Information must accompany 126 | the Minimal Corresponding Source and Corresponding Application 127 | Code. If you use option 4d1, you must provide the Installation 128 | Information in the manner specified by section 6 of the GNU GPL 129 | for conveying Corresponding Source.) 130 | 131 | 5. Combined Libraries. 132 | 133 | You may place library facilities that are a work based on the 134 | Library side by side in a single library together with other library 135 | facilities that are not Applications and are not covered by this 136 | License, and convey such a combined library under terms of your 137 | choice, if you do both of the following: 138 | 139 | a) Accompany the combined library with a copy of the same work based 140 | on the Library, uncombined with any other library facilities, 141 | conveyed under the terms of this License. 142 | 143 | b) Give prominent notice with the combined library that part of it 144 | is a work based on the Library, and explaining where to find the 145 | accompanying uncombined form of the same work. 146 | 147 | 6. Revised Versions of the GNU Lesser General Public License. 148 | 149 | The Free Software Foundation may publish revised and/or new versions 150 | of the GNU Lesser General Public License from time to time. Such new 151 | versions will be similar in spirit to the present version, but may 152 | differ in detail to address new problems or concerns. 153 | 154 | Each version is given a distinguishing version number. If the 155 | Library as you received it specifies that a certain numbered version 156 | of the GNU Lesser General Public License "or any later version" 157 | applies to it, you have the option of following the terms and 158 | conditions either of that published version or of any later version 159 | published by the Free Software Foundation. If the Library as you 160 | received it does not specify a version number of the GNU Lesser 161 | General Public License, you may choose any version of the GNU Lesser 162 | General Public License ever published by the Free Software Foundation. 163 | 164 | If the Library as you received it specifies that a proxy can decide 165 | whether future versions of the GNU Lesser General Public License shall 166 | apply, that proxy's public statement of acceptance of any version is 167 | permanent authorization for you to choose that version for the 168 | Library. 169 | 170 | 171 | ____________ __ ____ _ ____ _ 172 | / / __ ) \ \ \\ \ | __ )| |_ _ ___ / ___|_ __ __ ___ _| | 173 | | || _ \| | | |\ \ | _ \| | | | |/ _ \ | | '__/ _` \ \ /\ / / | 174 | | || |_) | | | |/ / | |_) | | |_| | __/ |___| | | (_| |\ V V /| | 175 | | ||____/| | | /_/ |____/|_|\__,_|\___|\____|_| \__,_| \_/\_/ |_| 176 | \_\ /_/_/_/ 177 | for Frida (Android) 178 | by Keith Makan @IOActive 179 | 180 | - simple frida script for interacting with and dumping info from bluetooth device objects and sockets 181 | - I left a little bit of breathing room for those who want to do other cool stuff 182 | 183 | _device_t = new Object(); 184 | _device_t._name=""; 185 | _device_t._address=""; 186 | _device_t._class=""; 187 | _device_t._type=""; 188 | _device_t._bond=""; 189 | 190 | */ 191 | /** should design a better console graphics library **/ 192 | 193 | function infoCursor(){ 194 | 195 | return "\033[2m"; 196 | } 197 | function lightGrayCursor(){ 198 | return "\033[37m"; 199 | } 200 | 201 | function lightGreenCursor(){ 202 | return "\033[92m"; 203 | } 204 | function greenCursor(){ 205 | return "\033[32m"; 206 | } 207 | 208 | function lightCyanCursor(){ 209 | return "\033[96m" 210 | } 211 | function lightBlueCursor(){ 212 | return "\033[94m" 213 | } 214 | function cyanCursor(){ 215 | return "\033[36m" 216 | } 217 | function blueCursor(){ 218 | return "\033[34m" 219 | } 220 | function closeCursor(){ 221 | return "\033[0m" 222 | } 223 | 224 | _device_list = [] //array of _device_t objects 225 | 226 | function add_new_device(_device_instance){ 227 | 228 | var _bluetoothDevice = _device_instance; 229 | var _bluetoothDevice_address = _bluetoothDevice.getAddress(); 230 | var _bluetoothDevice_class = _bluetoothDevice.getBluetoothClass(); 231 | var _bluetoothDevice_bondState = _bluetoothDevice.getBondState(); 232 | var _bluetoothDevice_name = _bluetoothDevice.getName(); 233 | var _bluetoothDevice_type = _bluetoothDevice.getType(); 234 | 235 | 236 | __device = new Object(); 237 | __device._address = _bluetoothDevice.getAddress(); 238 | __device._class = _bluetoothDevice.getBluetoothClass(); 239 | __device._bond = _bluetoothDevice.getBondState(); 240 | __device._name = _bluetoothDevice.getName(); 241 | __device._type = _bluetoothDevice.getType(); 242 | _device_list.push(__device); 243 | 244 | return __device; 245 | } 246 | 247 | function find_device(_address){ 248 | for (var index = 0; index < _device_list.length; index++){ 249 | if (_device_list[index]._address == _address){ 250 | return _device_list[index]; 251 | } 252 | } 253 | return undefined; 254 | } 255 | 256 | function make_spaces(length,spaces){ 257 | var line_length = spaces; 258 | var spaces = " "; 259 | var index = 0; 260 | for (;index < line_length - length;index++){ 261 | spaces+=" "; 262 | } 263 | return spaces; 264 | } 265 | 266 | function bluetoothDevice_MajorClassInfo(_majorClass){ 267 | //should probably use bit masks for this right? just return all the matching fields right? naah fuck it, this is javascript 268 | switch(_majorClass){ 269 | case(1024): return "("+_majorClass+") Audio/Video"; break; 270 | case(256): return "("+_majorClass+") Computer";break; //very very accurate description, very internet of things ready lol 271 | case(2304): return "("+_majorClass+") Health";break; 272 | case(1536): return "("+_majorClass+") Imaging";break; 273 | case(768): return "("+_majorClass+") Networking";break; 274 | case(1280): return "("+_majorClass+") Peripheral";break; 275 | case(512): return "("+_majorClass+") Phone";break; 276 | case(2048): return "("+_majorClass+") Toy"; break; 277 | case(7936): return "("+_majorClass+") Uncategorized";break; 278 | case(1792): return "("+_majorClass+") Wearable";break; 279 | case(0): return "Misc [0]";break; 280 | default: return "unknown ["+_majorClass+"]";break; 281 | } 282 | } 283 | function findServicesForClass(_class){ 284 | for (var index = 0;index < 3000;index++){ 285 | if (_class.hasService(index)){ 286 | console.log("[*]\t\t--"+bluetoothDevice_ServiceClassInfo(index)); 287 | } 288 | } 289 | 290 | } 291 | function bluetoothGattServiceCharacteristicInfo(_gatt_service){ 292 | 293 | } 294 | function bluetoothGattServiceInfo(_gatt_service){ 295 | console.log("[t]\t\t UUID: "+services[index].getUUID().toString()); 296 | return; 297 | } 298 | function bluetoothGattServerInfo(_gatt_server){ 299 | //expands on Gatt Services 300 | //produce a nice print out of the device gatt service and all its descriptors 301 | //UUID 302 | //Type 303 | //InstanceID 304 | //IncludedServices 305 | //Dump Characteristics 306 | try{ 307 | var services = _gatt_server.getServices(); 308 | 309 | } 310 | catch(err){ 311 | console.log("[x]\t\t"+err); 312 | return; 313 | } 314 | for (var index=0;index < services.length;index++){ 315 | console.log("[t]\t\t "+services[index].getUUID().toString()); 316 | } 317 | } 318 | function bluetoothDevice_ServiceClassInfo(_device_class){ //need to rename these but this sthe device class the one elow is te class object in java 319 | switch (_device_class){ 320 | case(1076): return "("+_device_class+") Audio Video Camcorder"; break; 321 | case(1056): return "("+_device_class+") Audio Video Car"; break; 322 | case(1032): return "("+_device_class_+") Audio Video Headphones"; break; 323 | case(1048): return "("+_device_class_+") Audio Video Hifi Audio"; break; 324 | case(1044): return "("+_device_class_+") Audio Video Loudspeaker"; break; 325 | case(1040): return "("+_device_class_+") Audio Video Microphone"; break; 326 | case(1052): return "("+_device_class_+") Audio Video Portable Audio"; break; 327 | case(1060): return "("+_device_class_+") Audio Video Set Top Box"; break; 328 | case(1024): return "("+_device_class_+") Audio Video Uncategorized"; break; 329 | case(1068): return "("+_device_class_+") Audio Video VCR"; break; 330 | case(1072): return "("+_device_class_+") Audio Video Video Camera"; break; 331 | case(1088): return "("+_device_class_+") Audio Video Conferencing"; break; 332 | case(1084): return "("+_device_class_+") Audio Video Display and Loudspeaker"; break; 333 | case(1096): return "("+_device_class_+") Audio Video Gaming/Toy";break; 334 | case(1080): return "("+_device_class_+") Audio Video Monitor"; break; 335 | case(1028): return "("+_device_class_+") Audio Video Wearable Headset";break; 336 | case(260): return "("+_device_class_+") Computer Desktop"; break; 337 | case(272): return "("+_device_class_+") Computer Handheld PC PDA"; break; 338 | case(268): return "("+_device_class_+") Computer Laptop"; break; 339 | case(276): return "("+_device_class_+") Computer Palm Size PC PDA"; break; 340 | case(264): return "("+_device_class_+") Computer Server"; break; 341 | case(256): return "("+_device_class_+") Computer Uncategorized"; break; 342 | case(280): return "("+_device_class_+") Computer Wearable"; break; 343 | case(2308): return "("+_device_class_+") Health Blood Pressure"; break; 344 | case(2332): return "("+_device_class_+") Health Data Display"; break; 345 | case(2320): return "("+_device_class_+") Health Glucose"; break; 346 | case(2324):return "("+_device_class_+") Health Pulse Oximeter"; break; 347 | case(2328): return "("+_device_class_+") Health Pulse Rate"; break; 348 | case(2312): return "("+_device_class_+") Health Thermometer"; break; 349 | case(2304): return "("+_device_class_+") Health Uncategorized"; break; 350 | case(2316): return "("+_device_class_+") Health Weighing"; break; 351 | case(516): return "("+_device_class_+") Phone Cellular";break; 352 | case(520): return "("+_device_class_+") Phone ISDN"; break; 353 | case(528): return "("+_device_class_+") Phone Modem or Gateway";break; 354 | case(524): return "("+_device_class_+") Phone Smart";break; 355 | case(512): return "("+_device_class_+") Phone Uncategorized";break; 356 | case(2064): return "("+_device_class_+") Toy Controller";break; 357 | case(2060): return "("+_device_class_+") Toy Action Figure"; break; 358 | case(2068): return "("+_device_class_+") Toy Game"; break; 359 | case(2052): return "("+_device_class_+") Toy Robot"; break; //will we one day have a sub-category for sentient robots? proabaly not anytime soon lol 360 | case(2048): return "("+_device_class_+") Toy Uncategorized";break; 361 | case(2056): return "("+_device_class_+") Toy Vehicle";break; 362 | case(1812): return "("+_device_class_+") Wearable Glasses";break; 363 | case(1808): return "("+_device_class_+") Wearable Helmet";break; 364 | case(1804): return "("+_device_class_+") Wearable Jacket";break; 365 | case(1800): return "("+_device_class_+") Wearable Pager";break; 366 | case(1792): return "("+_device_class_+") Wearable Uncategorized"; break; 367 | case(1796): return "("+_device_class_+") Wearable Wrist Watch";break; 368 | default: return "";break; 369 | } 370 | return ""; 371 | } 372 | function bluetoothClassInfo(_class){ 373 | //var _bluetoothClass_t = Java.cast("android.bluetooth.BluetoothClass",_class); 374 | //var _javaInteger = Java.use("java.lang.Integer"); 375 | //var _bluetoothDeviceClassMajor = Java.cast(_javaInteger,_class.getMajorDeviceClass()); 376 | var _bluetoothClass_t = ""; 377 | var _bluetoothDeviceClassMajor = ""; 378 | var _bluetoothDeviceClass = ""; 379 | 380 | try{ 381 | var _bluetoothClass_t = _class; 382 | var _bluetoothDeviceClassMajor = _bluetoothClass_t.getMajorDeviceClass(); 383 | //var _bluetoothDeviceServiceClass = device_service_class; 384 | //var _bluetoothDeviceHasService = _bluetoothClass_t.hasService(); need some argument for this not sure what yet 385 | var _bluetoothDeviceClass = _bluetoothClass_t.getDeviceClass(); 386 | 387 | } 388 | catch(err){ 389 | console.log("[x]\t\t"+err); 390 | } 391 | //console.log("\t\t[device::class] Major :" + bluetoothDevice_MajorClassInfo(_bluetoothDeviceClassMajor)); 392 | console.log("[*]\t\t"+lightBlueCursor()+"-- Class Major "+closeCursor()+" : "+ bluetoothDevice_MajorClassInfo(_bluetoothDeviceClassMajor)); 393 | //console.log("[*]\t\t"+lightBlueCursor()+"-- Device Class"+closeCursor()+" : "\ 394 | // + bluetoothDevice_ServiceClassInfo(_bluetoothDeviceClass)); 395 | //console.log("[*]\t\t"+lightBlueCursor()+"-- Service Class"+closeCursor()+" : "+ _bluetoothDeviceClass); 396 | //console.log("\t\t[device::class] hasService :" + _bluetoothDeviceHasService); 397 | //findServicesForClass(_class); 398 | } 399 | 400 | function bluetoothBondStateInfo(_type){ 401 | switch(_type){ 402 | case(12): return "("+_type+") Bonded"; break; 403 | case(11): return "("+_type+") Bonding";break; 404 | case(10): return "("+_type+") None";break; 405 | default: return "unknown ["+_type+"]"; break; 406 | } 407 | return; 408 | } 409 | 410 | function bluetoothConnectionTypeInfo(_type){ 411 | switch(_type){ 412 | case(1): return "("+_type+") L2CAP"; break; 413 | case(2): return "("+_type+") RFCOMM";break; 414 | case(3): return "("+_type+") SCO";break; 415 | default: return "unknown ["+_type+"]"; break; 416 | } 417 | return; 418 | } 419 | function bluetoothDeviceTypeInfo(_type){ 420 | switch(_type){ 421 | case(1): return "("+_type+") Classic"; break; 422 | case(2): return "("+_type+") Low Energy Only (LE Only)";break; 423 | case(3): return "("+_type+") Dual LE + Classic";break; 424 | case(0): return "("+_type+") unknown [0]"; break; 425 | default: return "unknown ["+_type+"]"; break; 426 | } 427 | return; 428 | } 429 | function interceptOutputStream(_outputStream_instance){ 430 | _outputStream_instance = Java.use("java.io.OutputStream"); 431 | _outputStream_instance.write = function(_byte){ 432 | console.log("[output stream]> "+_byte); 433 | } 434 | _outputStream_instance.write(41); 435 | } 436 | function interceptInputStream(_inputStream_instance){ 437 | _inputStream_instance.read.implementation = function(_byte){ 438 | console.log("[input stream]> "+_byte); 439 | } 440 | } 441 | 442 | function writeToOutputStream(_outputStream_instance,_bytes){ 443 | _outputStream_instance.write(_bytes); 444 | _outputStream_instance.flush(); 445 | 446 | } 447 | function bluetoothInputStreamInfo(_inputStream_instance){ 448 | var availableBytes = _inputStream_instance.available(); 449 | console.log("[*]\t\t- bytes available :"+availableBytes); 450 | } 451 | function bluetoothSocketInfo(_socket_instance){ 452 | console.log("[*]\t"+lightGrayCursor()+"getting info for '"+_socket_instance+"'"+closeCursor()); 453 | var _bluetoothSocket = _socket_instance; 454 | try{ 455 | 456 | var _isConnected = _bluetoothSocket.isConnected(); 457 | var _inputStream = _bluetoothSocket.getInputStream(); 458 | var _outputStream = _bluetoothSocket.getOutputStream(); 459 | var _connectionType = _bluetoothSocket.getConnectionType(); 460 | var _remoteDevice = _bluetoothSocket.getRemoteDevice(); 461 | 462 | spaces = 20; 463 | console.log("[*]\t"+lightGreenCursor()+"- isConnected"+make_spaces("- Connected".length,spaces)+closeCursor()+":"+_isConnected); //add different colors for each connected state 464 | console.log("[*]\t"+lightGreenCursor()+"- connection type"+make_spaces("- Connection Type".length,spaces)+closeCursor()+":"+bluetoothConnectionTypeInfo(_connectionType)); //different colors for connection state would be cool too 465 | 466 | try{ 467 | console.log("[*]\t"+lightGreenCursor()+"- connected device"+make_spaces("- Connected Device".length,spaces)+closeCursor()+":("+lightCyanCursor()+_remoteDevice.getAddress()+closeCursor()+") '"+greenCursor()+_remoteDevice.getName()+closeCursor()+"'");//definitely want to make this in light blue 468 | 469 | } 470 | catch(device_error){ 471 | console.log("[x]\t"+lightGrayCursor()+"- conneted device"+make_spaces("- Connected Device".length,spaces)+": device details not available"); 472 | } 473 | 474 | console.log("[*]\t"+lightGreenCursor()+"- InputStream"+make_spaces("- InputStream".length,spaces)+closeCursor()+":"+_inputStream); 475 | // bluetoothInputStreamInfo(_inputStream); 476 | // interceptInputStream(_inputStream); 477 | console.log("[*]\t"+lightGreenCursor()+"- OutputStream"+make_spaces("- OuputStream".length,spaces)+closeCursor()+":"+_outputStream); 478 | //writeToOutputStream(_outputStream,[41,41,41,41,41,41,41,41]); 479 | //interceptOutputStream(_outputStream); 480 | console.log(""); 481 | 482 | } 483 | catch(err){ 484 | console.log("\t\t"+err); 485 | return; 486 | } 487 | 488 | } 489 | 490 | function bluetoothServerSocketInfo(_socket_instance){ 491 | console.log("[*]\tgetting info for '"+_socket_instance+"'"); 492 | console.log("\t\t"+_socket_instance.toString()); 493 | 494 | } 495 | 496 | function bluetoothDeviceInfo(_device_instance){ 497 | 498 | console.log("[*]\tBluetooth device ['"+lightCyanCursor()+_device_instance+closeCursor()+"']"); 499 | 500 | var _bluetoothDevice = _device_instance; 501 | var _bluetoothDevice_address = _bluetoothDevice.getAddress(); 502 | var _bluetoothDevice_class = _bluetoothDevice.getBluetoothClass(); 503 | var _bluetoothDevice_bondState = _bluetoothDevice.getBondState(); 504 | var _bluetoothDevice_name = _bluetoothDevice.getName(); 505 | var _bluetoothDevice_type = _bluetoothDevice.getType(); 506 | 507 | //setPin and other fun still awaits - for now sticking to just simple reporting 508 | _bluetoothDevice = _device_instance; 509 | spaces = 16; 510 | 511 | console.log("[*]"+blueCursor()+"\t- Name"+closeCursor()+make_spaces("- Name".length,spaces)+":"+_bluetoothDevice.getName()); 512 | console.log("[*]"+blueCursor()+"\t- Address"+closeCursor()+make_spaces("- Address".length,spaces)+":"+_bluetoothDevice.getAddress()); 513 | console.log("[*]"+blueCursor()+"\t- Type"+closeCursor()+make_spaces("- Type".length,spaces)+":"+bluetoothDeviceTypeInfo(_bluetoothDevice.getType())); 514 | console.log("[*]"+blueCursor()+"\t- Device Class"+closeCursor()+make_spaces("- Device Class".length,spaces)+":"+_bluetoothDevice.getBluetoothClass()); 515 | bluetoothClassInfo(_bluetoothDevice.getBluetoothClass()); 516 | console.log("[*]"+blueCursor()+"\t- Bond State"+closeCursor()+make_spaces("- Bond State".length,spaces)+":"+bluetoothBondStateInfo(_bluetoothDevice.getBondState())); 517 | console.log(""); 518 | //need to dump data that can be read from this device 519 | } 520 | /* 521 | Main Java.perform loop for pulling out the relevant loaded classes and doing stuff with them. 522 | Basic work flow for Frida and android is: 523 | 1. pull objects from live class loader being used by the app 524 | 2. look for classses you want to do stuff with 525 | 3. Either replace them with other functions or pull data from the live objects in memory. 526 | 527 | here i simply target the bluetooth stack of objects because it avoids a tone of bluetooth protocol hacking in some cases. 528 | Also allows consultants to see the data being pushed out from the app. 529 | 530 | **/ 531 | VERSION="1.0.0" 532 | setTimeout(function(){ 533 | Java.perform(function(){ 534 | console.log("\n ---------- BlueCrawl : Bluetooth Metadata collector"); 535 | console.log(" v"+VERSION); 536 | 537 | console.log("\n finding loaded bluetooth classes"); 538 | 539 | Java.enumerateLoadedClasses({ 540 | onMatch: function(instance){ 541 | if (instance.split(".")[1] == "bluetooth"){ 542 | console.log("[->]\t"+lightBlueCursor()+instance+closeCursor()); 543 | } 544 | }, 545 | onComplete: function() {} 546 | }); 547 | 548 | Java.choose("android.bluetooth.BluetoothGattServer",{ 549 | onMatch: function (instance){ 550 | console.log("[*] "+infoCursor()+" android.bluetooth.BluetoothGattServer instance found"+closeCursor()+" :=> '"+instance+"'"); 551 | bluetoothGattServerInfo(instance); 552 | 553 | /* add_new_device(p_name,p_address,p_class,p_type,p_bond); 554 | add_new_device(instance); 555 | */ 556 | }, 557 | onComplete: function() { console.log("[*] -----");} 558 | }); 559 | 560 | Java.choose("android.bluetooth.BluetoothGattService",{ 561 | onMatch: function (instance){ 562 | console.log("[*] "+infoCursor()+" android.bluetooth.BluetoothGattService instance found"+closeCursor()+" :=> '"+instance+"'"); 563 | bluetoothGattServiceInfo(instance); 564 | 565 | /* add_new_device(p_name,p_address,p_class,p_type,p_bond); 566 | add_new_device(instance); 567 | */ 568 | }, 569 | onComplete: function() { console.log("[*] -----");} 570 | }); 571 | 572 | 573 | 574 | 575 | 576 | console.log("\n searching loaded classes for 'android.bluetooth.BluetoothSocket'..."); 577 | Java.choose("android.bluetooth.BluetoothSocket",{ 578 | onMatch: function (instance){ 579 | console.log("[*] "+infoCursor()+"'android.bluetooth.BluetoothSocket' instance found "+closeCursor()+"'"+instance+"'"); 580 | bluetoothSocketInfo(instance); 581 | }, 582 | onComplete: function() { console.log("[*] -----");} 583 | }); 584 | 585 | console.log("\n searching loaded classes for 'android.bluetooth.BluetoothServerSocket'..."); 586 | Java.choose("android.bluetooth.BluetoothServerSocket",{ 587 | onMatch: function (instance){ 588 | console.log("[*] "+infoCursor()+" 'android.bluetooth.BluetoothServerSocket' instance found"+closeCursor()+" '"+instance+"'"); 589 | //bluetoothServerSocketInfo(instance); 590 | }, 591 | onComplete: function() { console.log("[*] -----");} 592 | }); 593 | 594 | console.log("\n searching loaded classes for 'android.bluetooth.BluetoothDevice'..."); 595 | Java.choose("android.bluetooth.BluetoothDevice",{ 596 | onMatch: function (instance){ 597 | console.log("[*] "+infoCursor()+" android.bluetooth.BluetoothDevice instance found"+closeCursor()+" :=> '"+instance+"'"); 598 | bluetoothDeviceInfo(instance); 599 | 600 | /* add_new_device(p_name,p_address,p_class,p_type,p_bond); 601 | add_new_device(instance); 602 | */ 603 | }, 604 | onComplete: function() { console.log("[*] -----");} 605 | }); 606 | 607 | 608 | }); 609 | },0); 610 | --------------------------------------------------------------------------------