23 Commits

Author SHA1 Message Date
Florian Magin 692134f748 Document pwntools dev requirement for tests 2020-03-11 15:45:14 +01:00
Florian Magin c2166ce384 Mark trace test as flaky 2020-03-11 15:44:55 +01:00
Florian Magin a7266c819d Prevent orphaned file handle 2020-03-11 15:44:39 +01:00
Florian Magin f6704f904e Check for None instead of False due to changed function signature 2020-03-11 15:25:29 +01:00
Florian Magin 9ed9f6e1cc Fix and readd banner 2020-03-11 15:11:01 +01:00
Florian Magin efe3614ea5 Fix setup.py with new dependencies, entry point and test dependencies 2020-03-11 14:46:17 +01:00
Florian Magin e6b58865dc Add future as a dependency for now (should be removed while removing all remnants of python2 support) 2020-03-11 14:29:07 +01:00
Jiska Classen 8d14ab9485 confirmed that hcicore works on linux, and that adbcore works with and without serial mode. 2020-03-09 21:58:53 +01:00
Florian Magin f4f51a7952 Fix type issue that only workls during type checking 2020-03-09 16:46:25 +01:00
Florian Magin b409207a3e Remove old testcore 2020-03-07 15:38:18 +01:00
Florian Magin fa18727e69 Fix issues in macoscore.py 2020-03-07 15:36:29 +01:00
Florian Magin 6255023db8 Fix issues in ioscore.py 2020-03-07 15:35:47 +01:00
Florian Magin 796eb4cc03 Fix issues in hcicore.py 2020-03-07 15:30:41 +01:00
Florian Magin 6677b86e94 Fix various issues in hci.py 2020-03-07 15:26:18 +01:00
Florian Magin ca070290c5 Fix various issues in core.py 2020-03-07 15:24:14 +01:00
Florian Magin bc3d52f00e Fix various issues in cmds.py 2020-03-07 15:09:03 +01:00
Florian Magin d737068304 Fix type issues in adbcore.py 2020-03-07 14:22:30 +01:00
Florian Magin e8f6e94e1b Convert HCI COMND dict to proper enum for type checking and documentation purposes 2020-03-07 14:14:54 +01:00
Florian Magin 6e91f9c718 black -t py36 ./internalblue for code formatting 2020-03-07 13:12:52 +01:00
Florian Magin 361892bc06 Type annotations, typing fixes and simple refactors 2020-02-27 14:57:06 +01:00
Florian Magin 2ce2224421 Fix printing by regressing to using pwn directly 2020-02-27 14:55:02 +01:00
Florian Magin 104a35a79a Fix pwnlib related refactor so it still works with python2 2020-02-27 14:41:45 +01:00
Florian Magin e6b99906c9 Remove all 'from pwn import *' in internalblue code 2020-02-27 13:00:01 +01:00
43 changed files with 4332 additions and 3067 deletions
+31 -14
View File
@@ -1,27 +1,44 @@
from future import standard_library
standard_library.install_aliases()
import datetime
from queue import Queue
from typing import (
List,
Optional,
Any,
TYPE_CHECKING,
Tuple,
Union,
NewType,
Callable,
Dict,
)
# Address = NewType("Address", int)
Address = int
ConnectionNumber = NewType("ConnectionNumber", int)
ConnectionIndex = NewType("ConnectionIndex", int)
BluetoothAddress = NewType("BluetoothAddress", bytes)
ConnectionDict = NewType("ConnectionDict", Dict[str, Any])
HeapInformation = NewType("HeapInformation", Dict[str, Any])
QueueInformation = NewType("QueueInformation", Dict[str, Any])
try:
from queue import Queue
from typing import List, Optional, Any, TYPE_CHECKING, Tuple, Union, NewType, Callable, Dict
if TYPE_CHECKING:
import datetime
from internalblue.hci import HCI
from internalblue.core import InternalBlue
Address = NewType("Address", int)
Record = Tuple[HCI, int, int, int, Any, datetime.datetime]
FilterFunction = Callable[[Record], bool]
ConnectionNumber = NewType("ConnectionNumber", int)
ConnectionIndex = NewType("ConnectionIndex", int)
BluetoothAddress = NewType("BluetoothAddress", bytes)
ConnectionDict = NewType("ConnectionDict", Dict[str,Any])
HeapInformation = NewType("HeapInformation", Dict[str, Any])
QueueInformation = NewType('QueueInformation', Dict[str, Any])
Opcode = NewType('Opcode', int)
HCI_CMD = NewType('HCI_CMD', int)
Task = Tuple[HCI_CMD, bytes, Queue.Queue, Callable[[Record], bool]]
Opcode = NewType("Opcode", int)
HCI_CMD = NewType("HCI_CMD", int)
Task = Tuple[HCI_CMD, bytes, Queue, Callable[[Record], bool]]
Device = NewType("Device", Dict[str, Any])
"""{"dev_id": dev_id,
View File
+141 -64
View File
@@ -1,6 +1,13 @@
#!/usr/bin/env python2
import struct
from time import sleep
from typing import Optional
from future import standard_library
from pwnlib import adb
from pwnlib.exception import PwnlibException
standard_library.install_aliases()
from builtins import str
import datetime
@@ -10,17 +17,32 @@ import random
from internalblue import hci
from internalblue.utils import bytes_to_hex
from pwn import *
from internalblue.utils.pwnlib_wrapper import log, context, u32
from .core import InternalBlue
class ADBCore(InternalBlue):
def __init__(self, queue_size=1000, btsnooplog_filename='btsnoop.log', log_level='info', fix_binutils='True', serial=False, data_directory=".", replay=False):
super(ADBCore, self).__init__(queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory, replay)
self.hciport = None # hciport is the port number of the forwarded HCI snoop port (8872). The inject port is at hciport+1
self.serial = serial # use serial su busybox scripting and do not try bluetooth.default.so
def __init__(
self,
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
fix_binutils="True",
serial=False,
data_directory=".",
replay=False,
):
super(ADBCore, self).__init__(
queue_size,
btsnooplog_filename,
log_level,
fix_binutils,
data_directory,
replay,
)
self.hciport: Optional[int] = None # hciport is the port number of the forwarded HCI snoop port (8872). The inject port is at hciport+1
self.serial = serial # use serial su busybox scripting and do not try bluetooth.default.so
self.doublecheck = False
def device_list(self):
@@ -36,16 +58,18 @@ class ADBCore(InternalBlue):
return []
if self.replay:
return [(self, "adb_replay", 'adb: ReplayDevice' )]
return [(self, "adb_replay", "adb: ReplayDevice")]
# Check for connected adb devices
try:
adb_devices = adb.devices()
except ValueError:
log.info("Could not find devices with pwnlib. If you see devices with `adb devices`, try to remove the lines 'for field in fields[2:]:... = v' in `pwnlib/adb/adb.py`.")
log.info(
"Could not find devices with pwnlib. If you see devices with `adb devices`, try to remove the lines 'for field in fields[2:]:... = v' in `pwnlib/adb/adb.py`."
)
adb_devices = 0
except:
adb_devices = 0
if adb_devices == 0 or len(adb_devices) == 0:
log.info("No adb devices found.")
return []
@@ -59,7 +83,7 @@ class ADBCore(InternalBlue):
# Third index is the label which is shown in options(...)
device_list = []
for d in adb_devices:
device_list.append((self, d.serial, 'adb: %s (%s)' % (d.serial, d.model)))
device_list.append((self, d.serial, "adb: %s (%s)" % (d.serial, d.model)))
return device_list
@@ -77,7 +101,9 @@ class ADBCore(InternalBlue):
if not self.serial:
if not self._setupSockets():
log.info("Could not connect using Bluetooth module.")
log.info("Trying to set up connection for rooted smartphone with busybox installed.")
log.info(
"Trying to set up connection for rooted smartphone with busybox installed."
)
else:
return True # successfully finished setup with bluetooth.default.so
@@ -88,7 +114,9 @@ class ADBCore(InternalBlue):
# try again
if not self._setupSockets():
log.critical("No connection to target device.")
log.info("Check if:\n -> Bluetooth is active\n -> Bluetooth Stack has Debug Enabled\n -> BT HCI snoop log is activated\n -> USB debugging is authorized\n")
log.info(
"Check if:\n -> Bluetooth is active\n -> Bluetooth Stack has Debug Enabled\n -> BT HCI snoop log is activated\n -> USB debugging is authorized\n"
)
return False
return True
@@ -99,13 +127,17 @@ class ADBCore(InternalBlue):
"""
data = self.s_snoop.recv(16)
if(len(data) < 16):
if len(data) < 16:
return None
if(self.write_btsnooplog) and self.btsnooplog_file.tell() == 0:
if (self.write_btsnooplog) and self.btsnooplog_file.tell() == 0:
self.btsnooplog_file.write(data)
self.btsnooplog_file.flush()
btsnoop_hdr = (data[:8], u32(data[8:12],endian="big"),u32(data[12:16],endian="big"))
btsnoop_hdr = (
data[:8],
u32(data[8:12], endian="big"),
u32(data[12:16], endian="big"),
)
log.debug("BT Snoop Header: %s, version: %d, data link type: %d" % btsnoop_hdr)
return btsnoop_hdr
@@ -121,7 +153,9 @@ class ADBCore(InternalBlue):
this field as 0x00E03AB44A676000.
"""
time_betw_0_and_2000_ad = int("0x00E03AB44A676000", 16)
time_since_2000_epoch = datetime.timedelta(microseconds=time) - datetime.timedelta(microseconds=time_betw_0_and_2000_ad)
time_since_2000_epoch = datetime.timedelta(
microseconds=time
) - datetime.timedelta(microseconds=time_betw_0_and_2000_ad)
return datetime.datetime(2000, 1, 1) + time_since_2000_epoch
def _recvThreadFunc(self):
@@ -141,18 +175,23 @@ class ADBCore(InternalBlue):
context.log_level = self.log_level
# Read the record header
record_hdr = b''
while(not self.exit_requested and len(record_hdr) < 24):
record_hdr = b""
while not self.exit_requested and len(record_hdr) < 24:
try:
recv_data = self.s_snoop.recv(24 - len(record_hdr))
log.debug("recvThreadFunc: received bt_snoop data " + bytes_to_hex(recv_data))
log.debug(
"recvThreadFunc: received bt_snoop data "
+ bytes_to_hex(recv_data)
)
if len(recv_data) == 0:
log.info("recvThreadFunc: bt_snoop socket was closed by remote site. stopping recv thread...")
log.info(
"recvThreadFunc: bt_snoop socket was closed by remote site. stopping recv thread..."
)
self.exit_requested = True
break
record_hdr += recv_data
except socket.timeout:
pass # this is ok. just try again without error
pass # this is ok. just try again without error
if not record_hdr or len(record_hdr) != 24:
if not self.exit_requested:
@@ -164,27 +203,31 @@ class ADBCore(InternalBlue):
self.btsnooplog_file.write(record_hdr)
self.btsnooplog_file.flush()
orig_len, inc_len, flags, drops, time64 = struct.unpack( ">IIIIq", record_hdr)
orig_len, inc_len, flags, drops, time64 = struct.unpack(
">IIIIq", record_hdr
)
# Read the record data
record_data = bytearray()
while(not self.exit_requested and len(record_data) < inc_len):
while not self.exit_requested and len(record_data) < inc_len:
try:
recv_data = self.s_snoop.recv(inc_len - len(record_data))
if len(recv_data) == 0:
log.info("recvThreadFunc: bt_snoop socket was closed by remote site. stopping..")
log.info(
"recvThreadFunc: bt_snoop socket was closed by remote site. stopping.."
)
self.exit_requested = True
break
record_data += bytearray(recv_data)
except socket.timeout:
pass # this is ok. just try again without error
pass # this is ok. just try again without error
if not record_data or len(record_data) != inc_len:
if not self.exit_requested:
log.warn("recvThreadFunc: Cannot recv data. stopping.")
self.exit_requested = True
break
if self.write_btsnooplog:
self.btsnooplog_file.write(record_data)
self.btsnooplog_file.flush()
@@ -195,9 +238,18 @@ class ADBCore(InternalBlue):
parsed_time = None
# Put all relevant infos into a tuple. The HCI packet is parsed with the help of hci.py.
record = (hci.parse_hci_packet(record_data), orig_len, inc_len, flags, drops, parsed_time)
record = (
hci.parse_hci_packet(record_data),
orig_len,
inc_len,
flags,
drops,
parsed_time,
)
log.debug("_recvThreadFunc Recv: [" + str(parsed_time) + "] " + str(record[0]))
log.debug(
"_recvThreadFunc Recv: [" + str(parsed_time) + "] " + str(record[0])
)
# Put the record into all queues of registeredHciRecvQueues if their
# filter function matches.
@@ -205,8 +257,10 @@ class ADBCore(InternalBlue):
if filter_function == None or filter_function(record):
try:
queue.put(record, block=False)
except queue.Full:
log.warn("recvThreadFunc: A recv queue is full. dropping packets..")
except queue2k.Full:
log.warn(
"recvThreadFunc: A recv queue is full. dropping packets.."
)
# Call all callback functions inside registeredHciCallbacks and pass the
# record as argument.
@@ -215,9 +269,9 @@ class ADBCore(InternalBlue):
# Check if the stackDumpReceiver has noticed that the chip crashed.
# if self.stackDumpReceiver and self.stackDumpReceiver.stack_dump_has_happend:
# A stack dump has happend!
# log.warn("recvThreadFunc: The controller sent a stack dump.")
# self.exit_requested = True
# A stack dump has happend!
# log.warn("recvThreadFunc: The controller sent a stack dump.")
# self.exit_requested = True
log.debug("Receive Thread terminated.")
@@ -233,12 +287,17 @@ class ADBCore(InternalBlue):
# (with multiple attached Android devices) we must not hard code the
# forwarded port numbers. Therefore we choose the port numbers
# randomly and hope that they are not already in use.
self.hciport = random.randint(60000, 65534) # minus 1, as we are using hciport + 1
log.debug("_setupSockets: Selected random ports snoop=%d and inject=%d" % (self.hciport, self.hciport + 1))
self.hciport = random.randint(
60000, 65534
) # minus 1, as we are using hciport + 1
log.debug(
"_setupSockets: Selected random ports snoop=%d and inject=%d"
% (self.hciport, self.hciport + 1)
)
# Forward ports 8872 and 8873. Ignore log.info() outputs by the adb function.
saved_loglevel = context.log_level
context.log_level = 'warn'
context.log_level = "warn"
try:
adb.adb(["forward", "tcp:%d" % (self.hciport), "tcp:8872"])
adb.adb(["forward", "tcp:%d" % (self.hciport + 1), "tcp:8873"])
@@ -251,7 +310,7 @@ class ADBCore(InternalBlue):
# Connect to hci injection port
self.s_inject = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.s_inject.connect(('127.0.0.1', self.hciport + 1))
self.s_inject.connect(("127.0.0.1", self.hciport + 1))
self.s_inject.settimeout(0.5)
except socket.error:
log.warn("Could not connect to adb. Is your device authorized?")
@@ -259,16 +318,16 @@ class ADBCore(InternalBlue):
# Connect to hci snoop log port
self.s_snoop = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s_snoop.connect(('127.0.0.1', self.hciport))
self.s_snoop.connect(("127.0.0.1", self.hciport))
self.s_snoop.settimeout(0.5)
# Read btsnoop header
if (self._read_btsnoop_hdr() == None):
if self._read_btsnoop_hdr() == None:
log.warn("Could not read btsnoop header")
self.s_inject.close()
self.s_snoop.close()
self.s_inject = self.s_snoop = None
context.log_level = 'warn'
context.log_level = "warn"
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport)])
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport + 1)])
context.log_level = saved_loglevel
@@ -280,23 +339,25 @@ class ADBCore(InternalBlue):
Close s_snoop and s_inject sockets. Remove port forwarding with adb.
"""
if (self.s_inject != None):
if self.s_inject != None:
self.s_inject.close()
self.s_inject = None
if (self.s_snoop != None):
if self.s_snoop != None:
self.s_snoop.close()
self.s_snoop = None
saved_loglevel = context.log_level
context.log_level = 'warn'
try:
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport)])
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport + 1)])
except PwnlibException as e:
log.warn("Removing adb port forwarding failed: " + str(e))
return False
finally:
context.log_level = saved_loglevel
context.log_level = "warn"
if self.hciport is not None:
hciport = self.hciport
try:
adb.adb(["forward", "--remove", f"tcp:{hciport}"])
adb.adb(["forward", "--remove", f"tcp:{hciport + 1}"])
except PwnlibException as e:
log.warn("Removing adb port forwarding failed: " + str(e))
return False
finally:
context.log_level = saved_loglevel
def _setupSerialSu(self):
"""
@@ -320,36 +381,54 @@ class ADBCore(InternalBlue):
self.serial = True
saved_loglevel = context.log_level
context.log_level = 'warn'
context.log_level = "warn"
try:
# check dependencies
if adb.which('su') is None:
if adb.which("su") is None:
log.critical("su not found, rooted smartphone required!")
return False
if adb.process(['su', '-c', 'which', 'nc']).recvall() == '':
if adb.process(["su", "-c", "which", "nc"]).recvall() == "":
log.critical("nc not found, install busybox!")
return False
# automatically detect the proper serial device with lsof
logfile = adb.process(["su", "-c", "lsof | grep btsnoop_hci.log | awk '{print $NF}'"]).recvall().strip()
logfile = (
adb.process(
["su", "-c", "lsof | grep btsnoop_hci.log | awk '{print $NF}'"]
)
.recvall()
.strip()
.decode("utf-8")
)
log.info("Android btsnoop logfile %s...", logfile)
interface = adb.process(["su", "-c", "lsof | grep bluetooth | grep tty | awk '{print $NF}'"]).recvall().strip()
interface = (
adb.process(
["su", "-c", "lsof | grep bluetooth | grep tty | awk '{print $NF}'"]
)
.recvall()
.strip()
.decode("utf-8")
)
log.info("Android Bluetooth interface %s...", interface)
if logfile == '':
log.critical("Could not find Bluetooth logfile. Enable Bluetooth snoop logging.")
if logfile == "":
log.critical(
"Could not find Bluetooth logfile. Enable Bluetooth snoop logging."
)
return False
if interface == '':
if interface == "":
log.critical("Could not find Bluetooth interface. Enable Bluetooth.")
return False
# spawn processes
adb.process(["su", "-c", 'tail -f -n +0 %s | nc -l -p 8872' % logfile])
adb.process(["su", "-c", "tail -f -n +0 %s | nc -l -p 8872" % logfile])
adb.process(["su", "-c", "nc -l -p 8873 >/sdcard/internalblue_input.bin"])
adb.process(["su", "-c", "tail -f /sdcard/internalblue_input.bin >>%s" % interface])
adb.process(
["su", "-c", "tail -f /sdcard/internalblue_input.bin >>%s" % interface]
)
sleep(2)
except PwnlibException as e:
@@ -359,5 +438,3 @@ class ADBCore(InternalBlue):
context.log_level = saved_loglevel
return True
+123 -62
View File
@@ -29,8 +29,11 @@
from __future__ import print_function
import socket
import sys
from builtins import str
from pwn import *
import internalblue.utils.pwnlib_wrapper as pwnlib
import os
import traceback
import argparse
@@ -52,80 +55,116 @@ except:
HISTFILE = "_internalblue.hist"
def print_banner():
banner = """\
banner = r"""
____ __ _____ __
/ _/__ / /____ _______ ___ _/ / _ )/ /_ _____
_/ // _ \/ __/ -_) __/ _ \/ _ `/ / _ / / // / -_)
/___/_//_/\__/\__/_/ /_//_/\_,_/_/____/_/\_,_/\__/
type <help> for usage information!\n\n"""
type <help> for usage information!
"""
for line in banner:
term.output(text.blue(line))
pwnlib.term.output(pwnlib.text.blue(line))
def commandLoop(internalblue, init_commands=None):
cmdstack = init_commands.split(';')[::-1] if init_commands else None
cmdstack = init_commands.split(";")[::-1] if init_commands else None
while internalblue.running and not internalblue.exit_requested:
cmd_instance = None
try:
if cmdstack:
cmdline = cmdstack.pop().strip()
else:
cmdline = term.readline.readline(prompt='> ').strip().decode('utf-8')
cmdword = cmdline.split(' ')[0].split('=')[0]
if(cmdword == ''):
cmdline = (
pwnlib.term.readline.readline(prompt="> ").strip().decode("utf-8")
)
cmdword = cmdline.split(" ")[0].split("=")[0]
if cmdword == "":
continue
log.debug("Command Line: [[" + cmdword + "]] " + cmdline)
pwnlib.log.debug("Command Line: [[" + cmdword + "]] " + cmdline)
matching_cmd = cmds.findCmd(cmdword)
if matching_cmd == None:
log.warn("Command unknown: " + cmdline)
pwnlib.log.warn("Command unknown: " + cmdline)
continue
cmd_instance = matching_cmd(cmdline, internalblue)
if(not cmd_instance.work()):
log.warn("Command failed: " + str(cmd_instance))
if not cmd_instance.work():
pwnlib.log.warn("Command failed: " + str(cmd_instance))
except ValueError as e:
log.warn("commandLoop: ValueError: " + str(e))
pwnlib.log.warn("commandLoop: ValueError: " + str(e))
raise
except KeyboardInterrupt:
if(cmd_instance != None):
if cmd_instance != None:
cmd_instance.abort_cmd()
else:
log.info("Got Ctrl-C; exiting...")
pwnlib.log.info("Got Ctrl-C; exiting...")
internalblue.exit_requested = True
break
except AssertionError as e:
raise
except socket.error as e:
if e.args == (1, "Operation not permitted"):
log.critical("Received an 'Operation not permitted' socket.error, you might need root for the command '{}'".format(cmdline))
log.critical(traceback.format_exc())
pwnlib.log.critical(
"Received an 'Operation not permitted' socket.error, you might need root for the command '{}'".format(
cmdline
)
)
pwnlib.log.critical(traceback.format_exc())
except Exception as e:
internalblue.exit_requested = True # Make sure all threads terminate
log.critical("Uncaught exception (%s). Abort." % str(e))
internalblue.exit_requested = True # Make sure all threads terminate
pwnlib.log.critical("Uncaught exception (%s). Abort." % str(e))
print(traceback.format_exc())
raise
cmd_instance = None
def _parse_argv(argv):
parser = argparse.ArgumentParser()
parser.add_argument("--data-directory", "-d", help="Set data directory. Default: ~/.internalblue")
parser.add_argument("--verbose", "-v", help="Set log level to DEBUG", action="store_true")
parser.add_argument("--ios-device", "-i", help="Tell internalblue to connect to a remote iPhone HCI socket. Specify socket IP address and port (i.e., 172.20.10.1:1234).")
parser.add_argument("--serialsu", "-s", help="On ADB, directly try su/serial/busybox scripting, if you do not have a special bluetooth.default.so file.", action="store_true")
parser.add_argument("--testdevice", "-t", help="Use a dummy test device to execute testcases", action="store_true")
parser.add_argument(
"--data-directory", "-d", help="Set data directory. Default: ~/.internalblue"
)
parser.add_argument(
"--verbose", "-v", help="Set log level to DEBUG", action="store_true"
)
parser.add_argument(
"--ios-device",
"-i",
help="Tell internalblue to connect to a remote iPhone HCI socket. Specify socket IP address and port (i.e., 172.20.10.1:1234).",
)
parser.add_argument(
"--serialsu",
"-s",
help="On ADB, directly try su/serial/busybox scripting, if you do not have a special bluetooth.default.so file.",
action="store_true",
)
parser.add_argument("--trace", help="Trace hci connection")
parser.add_argument("--device", help="Specify device/core to be used")
parser.add_argument("--commands", "-c", help="CLI command to run before prompting, seperated by ';' (used for easier testing)")
parser.add_argument("--replay", help="Intercept and replace every communication with the core with the one in the specified file")
parser.add_argument("--save", help="Store a trace into the file that can be used with --replay")
parser.add_argument(
"--commands",
"-c",
help="CLI command to run before prompting, seperated by ';' (used for easier testing)",
)
parser.add_argument(
"--replay",
help="Intercept and replace every communication with the core with the one in the specified file",
)
parser.add_argument(
"--save", help="Store a trace into the file that can be used with --replay"
)
return parser.parse_args(argv)
# Entry point for the `internalblue` command entry point defined in setup.py
def internalblue_entry_point():
print_banner()
return internalblue_cli(sys.argv[1:])
# Main Program Start
def internalblue_cli(argv, args=None):
#print_banner()
# print_banner()
args = args or _parse_argv(argv)
@@ -146,90 +185,113 @@ def internalblue_cli(argv, args=None):
for cmd in cmds.getCmdList():
for keyword in cmd.keywords:
cmd_keywords.append(keyword)
readline_completer = term.completer.LongestPrefixCompleter(words=cmd_keywords)
term.readline.set_completer(readline_completer)
readline_completer = pwnlib.term.completer.LongestPrefixCompleter(
words=cmd_keywords
)
pwnlib.term.readline.set_completer(readline_completer)
if args.trace:
from .socket_hooks import hook
from internalblue import socket_hooks
HookClass = getattr(socket_hooks, args.trace)
hook(HCICore, HookClass)
hook(ADBCore, HookClass)
elif args.save:
from .socket_hooks import hook, TraceToFileHook
hook(HCICore, TraceToFileHook, filename=args.save)
hook(ADBCore, TraceToFileHook, filename=args.save)
# Initalize cores and get devices
# As macOS has additional dependencies (objc), only import it here if needed
connection_methods = [] # type: List[InternalBlue]
connection_methods = [] # type: List[InternalBlue]
if args.replay:
from .socket_hooks import hook, ReplaySocket
from .macoscore import macOSCore
replay_devices = ['macos_replay', 'adb_replay', 'hci_replay', 'ios_replay']
replay_devices = ["macos_replay", "adb_replay", "hci_replay", "ios_replay"]
if args.device == "macos_replay":
from .macoscore import macOSCore
hook(macOSCore, ReplaySocket, filename=args.replay)
connection_methods = [macOSCore(log_level=log_level, data_directory=data_directory, replay=True)]
connection_methods = [
macOSCore(
log_level=log_level, data_directory=data_directory, replay=True
)
]
elif args.device == "hci_replay":
hook(HCICore, ReplaySocket, filename=args.replay)
connection_methods = [HCICore(log_level=log_level, data_directory=data_directory, replay=True)]
connection_methods = [
HCICore(log_level=log_level, data_directory=data_directory, replay=True)
]
elif args.device == "adb_replay":
hook(ADBCore, ReplaySocket, filename=args.replay)
connection_methods = [ADBCore(log_level=log_level, data_directory=data_directory, replay=True)]
connection_methods = [
ADBCore(log_level=log_level, data_directory=data_directory, replay=True)
]
elif args.device == "ios_replay":
raise NotImplementedError("ios replay is not implemented yet")
else:
raise ValueError("--device is required with --replay and has to be one of {}".format(replay_devices))
raise ValueError(
"--device is required with --replay and has to be one of {}".format(
replay_devices
)
)
elif args.ios_device:
from .ioscore import iOSCore
connection_methods = [iOSCore(args.ios_device, log_level=log_level, data_directory=data_directory)]
elif args.testdevice:
from .testcore import testCore
connection_methods = [testCore(log_level=log_level, data_directory=data_directory)]
connection_methods = [
iOSCore(args.ios_device, log_level=log_level, data_directory=data_directory)
]
elif platform == "darwin":
from .macoscore import macOSCore
connection_methods = [
macOSCore(log_level=log_level, data_directory=data_directory, replay=(args.replay and args.device == 'mac')),
ADBCore(log_level=log_level, data_directory=data_directory)]
macOSCore(
log_level=log_level,
data_directory=data_directory,
replay=(args.replay and args.device == "mac"),
),
ADBCore(log_level=log_level, data_directory=data_directory),
]
if args.trace:
hook(macOSCore, HookClass)
elif args.save:
hook(macOSCore, TraceToFileHook, filename=args.save)
else:
connection_methods = [
ADBCore(log_level=log_level, data_directory=data_directory, serial=args.serialsu),
HCICore(log_level=log_level, data_directory=data_directory)]
ADBCore(
log_level=log_level, data_directory=data_directory, serial=args.serialsu
),
HCICore(log_level=log_level, data_directory=data_directory),
]
devices = [] # type: List[DeviceTuple]
devices = [] # type: List[DeviceTuple]
for connection_method in connection_methods:
devices.extend(connection_method.device_list())
device = None # type: Optional[DeviceTuple]
device = None # type: Optional[DeviceTuple]
if len(devices) > 0:
if args.replay:
# There should only be one device that was created when --replay was passed
device = devices[0]
elif args.device:
matching_devices = [ dev for dev in devices if dev[1] == args.device]
matching_devices = [dev for dev in devices if dev[1] == args.device]
if len(matching_devices) > 1:
log.critical("Found multiple matching devices")
pwnlib.log.critical("Found multiple matching devices")
exit(-1)
elif len(matching_devices) == 1:
log.info("Found device is: {}".format(matching_devices[0]))
pwnlib.log.info("Found device is: {}".format(matching_devices[0]))
device = matching_devices[0]
else:
log.critical("No matching devices found")
pwnlib.log.critical("No matching devices found")
exit(-1)
elif len(devices) == 1:
device = devices[0]
else:
i = options('Please specify device:', [d[2] for d in devices], 0)
i = pwnlib.options("Please specify device:", [d[2] for d in devices], 0)
device = devices[i]
# Setup device
@@ -238,16 +300,16 @@ def internalblue_cli(argv, args=None):
# Restore readline history:
if os.path.exists(reference.data_directory + "/" + HISTFILE):
readline_history = read(reference.data_directory + "/" + HISTFILE)
term.readline.history = readline_history.split(b'\n')
readline_history = pwnlib.read(reference.data_directory + "/" + HISTFILE)
pwnlib.term.readline.history = readline_history.split(b"\n")
# Connect to device
if not reference.connect():
log.critical("No connection to target device.")
pwnlib.log.critical("No connection to target device.")
exit(-1)
# Enter command loop (runs until user quits)
log.info("Starting commandLoop for reference {}".format(reference))
pwnlib.log.info("Starting commandLoop for reference {}".format(reference))
commandLoop(reference, init_commands=args.commands)
# shutdown connection
@@ -260,9 +322,8 @@ def internalblue_cli(argv, args=None):
# f.close()
# Cleanup
log.info("Goodbye")
pwnlib.log.info("Goodbye")
if __name__ == "__main__":
internalblue_cli(sys.argv[1:])
+962 -572
View File
File diff suppressed because it is too large Load Diff
+666 -304
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
from .fw import FirmwareDefinition
+102 -18
View File
@@ -25,10 +25,81 @@
from builtins import hex
from builtins import object
from types import ModuleType
from typing import List
from internalblue import Address
from pwn import log
class MemorySection(object):
"""
All firmwares have memory sections that can be RAM, ROM or neither of both.
"""
def __init__(self, start_addr, end_addr, is_rom, is_ram):
self.start_addr: Address = start_addr
self.end_addr: Address = end_addr
self.is_rom: bool = is_rom
self.is_ram: bool = is_ram
def size(self) -> int:
return self.end_addr - self.start_addr
class FirmwareDefinition:
DEVICE_NAME: Address
BD_ADDR: Address
SECTIONS: List[MemorySection]
TRACEPOINT_BODY_ASM_SNIPPET: str
TRACEPOINT_HOOKS_LOCATION: int
TRACEPOINT_RAM_DUMP_PKT_COUNT = None
CONNECTION_STRUCT_LENGTH: int
FW_NAME: str
QUEUE_NAMES: List[str]
QUEUE_HEAD: Address
BLOC_HEAD: Address
SENDLCP_CODE_BASE_ADDRESS: Address
SENDLCP_ASM_CODE: str
SENDLMP_CODE_BASE_ADDRESS: Address
SENDLMP_ASM_CODE: str
FUZZLMP_HOOK_ADDRESS: Address
FUZZLMP_CODE_BASE_ADDRESS: Address
FUZZLMP_ASM_CODE: str
CONNECTION_LIST_ADDRESS: Address
CONNECTION_ARRAY_ADDRESS: Address
CONNECTION_MAX: int
PATCHRAM_VALUE_TABLE_ADDRESS: Address
PATCHRAM_TARGET_TABLE_ADDRESS: Address
PATCHRAM_ENABLED_BITMAP_ADDRESS: Address
PATCHRAM_ALIGNED: bool
PATCHRAM_NUMBER_OF_SLOTS: int
LAUNCH_RAM_PAUSE = None
READ_MEM_ALIGNED_ASM_LOCATION: Address
READ_MEM_ALIGNED_ASM_SNIPPET: str
TRACEPOINT_HOOK_SIZE = None
TRACEPOINT_BODY_ASM_LOCATION: Address
TRACEPOINT_HOOK_ASM = None
class Firmware(object):
firmware: FirmwareDefinition
def __init__(self, version=None, iOS=False):
"""
Load and initialize the actual firmware add-ons for Nexus 5, Raspi3, etc.
@@ -37,40 +108,53 @@ class Firmware(object):
"""
self.version = version
self.firmware = None
if version:
# get LMP Subversion
log.info("Chip identifier: 0x%04x (%03d.%03d.%03d)" %
(version, version >> 13, (version & 0xf00) >> 8, version & 0xff))
log.info(
"Chip identifier: 0x%04x (%03d.%03d.%03d)"
% (version, version >> 13, (version & 0xF00) >> 8, version & 0xFF)
)
try:
# Fix for duplicate version number of evaluation board / iPhones
if iOS and version==0x420e:
self.firmware = __import__(__name__ + '_' + hex(version) + '_iphone', fromlist=[''])
if iOS and version == 0x420E:
self.firmware = self._module_to_firmware_definition(
__import__(
__name__ + "_" + hex(version) + "_iphone", fromlist=[""]
)
)
log.info("Using fw_" + hex(version) + "_iphone.py")
else:
self.firmware = __import__(__name__ + '_' + hex(version), fromlist=[''])
self.firmware = self._module_to_firmware_definition(
__import__(__name__ + "_" + hex(version), fromlist=[""])
)
log.info("Using fw_" + hex(version) + ".py")
except ImportError:
self.firmware = None
pass
if not version or not self.firmware:
self.firmware = __import__(__name__ + '_default', fromlist=[''])
self.firmware = self._module_to_firmware_definition(
__import__(__name__ + "_default", fromlist=[""])
)
log.info("Loaded firmware information for " + self.firmware.FW_NAME + ".")
def _module_to_firmware_definition(self, fw: ModuleType) -> FirmwareDefinition:
"""
Wrap existing usages where the module was used and extract the new FirmwareDefinition class
class MemorySection(object):
"""
All firmwares have memory sections that can be RAM, ROM or neither of both.
"""
def __init__(self, start_addr, end_addr, is_rom, is_ram):
self.start_addr = start_addr
self.end_addr = end_addr
self.is_rom = is_rom
self.is_ram = is_ram
:param fw:
:return:
"""
_types = {
name: cls
for name, cls in fw.__dict__.items()
if isinstance(cls, type)
and issubclass(cls, FirmwareDefinition)
and not cls is FirmwareDefinition
}
def size(self):
return self.end_addr - self.start_addr
if len(_types) == 1:
return list(_types.values())[0]
+17 -15
View File
@@ -1,4 +1,5 @@
from __future__ import absolute_import
# fw_0x420e.py
#
# Generic firmware file in case we do not know something...
@@ -29,35 +30,36 @@ FW_NAME = "BCM4375B1"
# Device Infos
DEVICE_NAME = 0x207f2a
BD_ADDR = 0x2026e2
DEVICE_NAME = 0x207F2A
BD_ADDR = 0x2026E2
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x0013ffff, True, False), # Internal ROM
MemorySection(0x00160000, 0x0017ffff, False, True), # Patches
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
MemorySection(0x00300000, 0x0037ffff, False, True),
]
SECTIONS = [
MemorySection(0x00000000, 0x0013FFFF, True, False), # Internal ROM
MemorySection(0x00160000, 0x0017FFFF, False, True), # Patches
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
MemorySection(0x00300000, 0x0037FFFF, False, True),
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
BLOC_HEAD = 0x20075c
BLOC_HEAD = 0x20075C
BLOC_NG = True
# Assembler snippet for tracepoints
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
# TODO S10e does no longer have a patch uninstall function... writemem works to remove patches, but copying
# Assembly of the original function from an eval board does not work...
#TRACEPOINT_BODY_ASM_LOCATION = 0x00218300
#TRACEPOINT_HOOKS_LOCATION = 0x00218500
#TRACEPOINT_HOOK_SIZE = 40
# TRACEPOINT_BODY_ASM_LOCATION = 0x00218300
# TRACEPOINT_HOOKS_LOCATION = 0x00218500
# TRACEPOINT_HOOK_SIZE = 40
TRACEPOINT_HOOK_ASM = """
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
+27 -25
View File
@@ -28,6 +28,7 @@
# Firmware Infos
# This runs on Rasperry Pi 3
from builtins import object
FW_NAME = "BCM43430A1"
# Device Infos
@@ -45,42 +46,44 @@ class MemorySection(object):
def size(self):
return self.end_addr - self.start_addr
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x0, 0x90000, True , False),
MemorySection(0xd0000, 0xd8000, False, True ),
#MemorySection(0xe0000, 0x1f0000, True , False),
MemorySection(0x200000, 0x21ffff, False, True ),
#MemorySection(0x260000, 0x268000, True , False), # might crash? issue 14
#MemorySection(0x280000, 0x2a0000, True , False),
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x360000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
#MemorySection(0x680000, 0x800000, False, False)
]
SECTIONS = [
MemorySection(0x0, 0x90000, True, False),
MemorySection(0xD0000, 0xD8000, False, True),
# MemorySection(0xe0000, 0x1f0000, True , False),
MemorySection(0x200000, 0x21FFFF, False, True),
# MemorySection(0x260000, 0x268000, True , False), # might crash? issue 14
# MemorySection(0x280000, 0x2a0000, True , False),
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x360000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
# MemorySection(0x680000, 0x800000, False, False)
]
# Connection Structure and Table
#CONNECTION_LIST_ADDRESS = 0x204ba8
CONNECTION_MAX = 11
# CONNECTION_LIST_ADDRESS = 0x204ba8
CONNECTION_MAX = 11
CONNECTION_STRUCT_LENGTH = 0x150 # TODO
# Patchram
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = False
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = False
# Heap
BLOC_HEAD = 0x200588 # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
BLOC_HEAD = 0x200588 # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
# Snippet for sendLcpPacket()
SENDLCP_CODE_BASE_ADDRESS = 0x21a000
SENDLCP_CODE_BASE_ADDRESS = 0x21A000
SENDLCP_ASM_CODE = """
push {r4,lr}
@@ -96,4 +99,3 @@ SENDLCP_ASM_CODE = """
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
payload: // Note: the payload will be appended here by the sendLmpPacket() function
"""
+20 -20
View File
@@ -1,4 +1,5 @@
from __future__ import absolute_import
# fw_0x420e.py
#
# Generic firmware file in case we do not know something...
@@ -20,27 +21,26 @@ from __future__ import absolute_import
# liability, whether in an action of contract, tort or otherwise, arising from,
# out of or in connection with the Software or the use or other dealings in the
# Software.
from .fw import MemorySection
# Firmware Infos
# Evaluation Kit CYW20706
FW_NAME = "CYW20706"
from .fw import MemorySection, FirmwareDefinition
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x000c7fff, True, False), # Internal ROM
MemorySection(0x000d0000, 0x000dffff, False, True ),
MemorySection(0x00200000, 0x00247fff, False, True), # Internal Memory Cortex M3
]
# Patchram
#PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
#PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
#PATCHRAM_VALUE_TABLE_ADDRESS = 0x0d0000
#PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = True
# only seems to work 4-byte aligned here ...
class CYW20706(FirmwareDefinition):
# Firmware Infos
# Evaluation Kit CYW20706
FW_NAME = "CYW20706"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x000C7FFF, True, False), # Internal ROM
MemorySection(0x000D0000, 0x000DFFFF, False, True),
MemorySection(0x00200000, 0x00247FFF, False, True), # Internal Memory Cortex M3
]
# Patchram
# PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
# PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
# PATCHRAM_VALUE_TABLE_ADDRESS = 0x0d0000
# PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = True
# only seems to work 4-byte aligned here ...
+13 -11
View File
@@ -1,4 +1,5 @@
from __future__ import absolute_import
# fw_0x420e.py
#
# Generic firmware file in case we do not know something...
@@ -30,18 +31,19 @@ FW_NAME = "CYW20819A1"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x001fffff, True, False), # Internal ROM
MemorySection(0x00200000, 0x0024ffff, False, True), # Internal Memory Cortex M3
MemorySection(0x00270000, 0x0027ffff, False, True), # Internal Memory Patchram Contents
MemorySection(0x00310000, 0x00321fff, False, True), # HW Regs Cortex M3 (readable)
]
SECTIONS = [
MemorySection(0x00000000, 0x001FFFFF, True, False), # Internal ROM
MemorySection(0x00200000, 0x0024FFFF, False, True), # Internal Memory Cortex M3
MemorySection(
0x00270000, 0x0027FFFF, False, True
), # Internal Memory Patchram Contents
MemorySection(0x00310000, 0x00321FFF, False, True), # HW Regs Cortex M3 (readable)
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x270000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
PATCHRAM_VALUE_TABLE_ADDRESS = 0x270000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
# only seems to work 4-byte aligned here ...
+7 -7
View File
@@ -30,13 +30,13 @@ from .fw import MemorySection
FW_NAME = "BCM20702A1 (USB Bluetooth dongle)"
# Device Infos
#DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
#BD_ADDR = 0x280CA4 # rm_deviceBDAddr
# DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
# BD_ADDR = 0x280CA4 # rm_deviceBDAddr
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x5ffff, True, False), # Internal ROM
MemorySection(0x80000, 0x9bfff, False, True), # Internal RAM
]
BLOC_HEAD = 0x3166c
SECTIONS = [
MemorySection(0x00000000, 0x5FFFF, True, False), # Internal ROM
MemorySection(0x80000, 0x9BFFF, False, True), # Internal RAM
]
BLOC_HEAD = 0x3166C
+13 -12
View File
@@ -34,15 +34,16 @@ FW_NAME = "BCM20703A2 (MacBook Pro 2016)"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x0, 0xc7fff, True, False), #0x000c0a97
MemorySection(0xd0000, 0xe0000, False, False), #0x000dd78c
MemorySection(0x200000, 0x240000, False, True ), #0x00217a38
MemorySection(0x260000, 0x268fff, True, False), #0x0026841d
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x338000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False)
]
SECTIONS = [
MemorySection(0x0, 0xC7FFF, True, False), # 0x000c0a97
MemorySection(0xD0000, 0xE0000, False, False), # 0x000dd78c
MemorySection(0x200000, 0x240000, False, True), # 0x00217a38
MemorySection(0x260000, 0x268FFF, True, False), # 0x0026841d
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x338000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
]
+31 -30
View File
@@ -34,27 +34,30 @@ FW_NAME = "BCM4358A3"
# Device Infos
DEVICE_NAME = 0x213994 # [type: 1byte] [len: 1byte] [name: len byte] #works
BD_ADDR = 0x201C48 #works
BD_ADDR = 0x201C48 # works
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x0, 0x9ef00, True , False),
MemorySection(0xd0000, 0xd8000, False, True ), # Patchram values with actual code / hooks
#MemorySection(0xe0000, 0x1e0000, True , False), # all zero
MemorySection(0x200000, 0x22a000, False, True ),
MemorySection(0x260000, 0x268000, True , False),
#MemorySection(0x280000, 0x2a0000, True , False), # all zero
MemorySection(0x300000, 0x301000, False, False),
MemorySection(0x310000, 0x318000, False, True ), # Patchram addresses
MemorySection(0x318000, 0x322000, False, False),
MemorySection(0x324000, 0x368000, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
#MemorySection(0x680000, 0x800000, False, False)
#MemorySection(0x770000, 0x78ffff, False, False), #TODO maybe more, but all zero
]
SECTIONS = [
MemorySection(0x0, 0x9EF00, True, False),
MemorySection(
0xD0000, 0xD8000, False, True
), # Patchram values with actual code / hooks
# MemorySection(0xe0000, 0x1e0000, True , False), # all zero
MemorySection(0x200000, 0x22A000, False, True),
MemorySection(0x260000, 0x268000, True, False),
# MemorySection(0x280000, 0x2a0000, True , False), # all zero
MemorySection(0x300000, 0x301000, False, False),
MemorySection(0x310000, 0x318000, False, True), # Patchram addresses
MemorySection(0x318000, 0x322000, False, False),
MemorySection(0x324000, 0x368000, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
# MemorySection(0x680000, 0x800000, False, False)
# MemorySection(0x770000, 0x78ffff, False, False), #TODO maybe more, but all zero
]
# Connection Struct and Table
@@ -63,19 +66,19 @@ SECTIONS = [ MemorySection(0x0, 0x9ef00, True , False),
# address 0x21AD5C holds a list with pointers to connection structs!
# CONNECTION_ARRAY_ADDRESS = 0x21ad88 #potentially the first valid address... but not part of an array
# CONNECTION_ARRAY_SIZE = 11 #is still 11 for Nexus 6P, but no longer hard-coded
CONNECTION_LIST_ADDRESS = 0x21AD5C
CONNECTION_MAX = 11
CONNECTION_STRUCT_LENGTH = 0x168 #??
CONNECTION_LIST_ADDRESS = 0x21AD5C
CONNECTION_MAX = 11
CONNECTION_STRUCT_LENGTH = 0x168 # ??
# Patchram
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
PATCHRAM_NUMBER_OF_SLOTS = 192
PATCHRAM_ALIGNED = False #we can use standard ReadRAM HCI on Nexus 6P
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
PATCHRAM_NUMBER_OF_SLOTS = 192
PATCHRAM_ALIGNED = False # we can use standard ReadRAM HCI on Nexus 6P
LAUNCH_RAM_PAUSE = 8 # bugfix: pause between multiple readMemAligned() calls in seconds
LAUNCH_RAM_PAUSE = 8 # bugfix: pause between multiple readMemAligned() calls in seconds
# not a problem: doing multiple writeMem in a row
# the thing that crashes: executing multiple launchRam() in a row: sendhcicmd 0xfc4e 0x473CC
# crashes even when executing 0x5E860 twice, which is just a nullsub
@@ -83,8 +86,8 @@ LAUNCH_RAM_PAUSE = 8 # bugfix: pause between multiple readMemAligned() calls in
# Snippet for sendLmpPacket()
SENDLMP_CODE_BASE_ADDRESS = 0xd5130
#TODO already works except for correct mac address - so still a problem with the connection #
SENDLMP_CODE_BASE_ADDRESS = 0xD5130
# TODO already works except for correct mac address - so still a problem with the connection #
SENDLMP_ASM_CODE = """
push {r4,lr}
@@ -129,7 +132,7 @@ SENDLMP_ASM_CODE = """
"""
# Assembler snippet for the readMemAligned() function
READ_MEM_ALIGNED_ASM_LOCATION = 0xd5030
READ_MEM_ALIGNED_ASM_LOCATION = 0xD5030
READ_MEM_ALIGNED_ASM_SNIPPET = """
push {r4, lr}
@@ -166,5 +169,3 @@ READ_MEM_ALIGNED_ASM_SNIPPET = """
b 0x20F4 // send_hci_event()
"""
+11 -8
View File
@@ -29,17 +29,20 @@ FW_NAME = "BCM4345B0"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x000c07ff, True, False), # Internal ROM
MemorySection(0x000d0000, 0x000dffff, False, True), # Internal Memory Patchram Contents
MemorySection(0x00200400, 0x00201cff, False, True), # Internal Memory Cortex M3
]
SECTIONS = [
MemorySection(0x00000000, 0x000C07FF, True, False), # Internal ROM
MemorySection(
0x000D0000, 0x000DFFFF, False, True
), # Internal Memory Patchram Contents
MemorySection(0x00200400, 0x00201CFF, False, True), # Internal Memory Cortex M3
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000 # needs to be aligned read
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000 # needs to be aligned read
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = True
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = True
# Assembler snippet for the readMemAligned() function
+13 -10
View File
@@ -29,19 +29,22 @@ FW_NAME = "BCM4347B0"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x00100000, True, False), # Internal ROM
MemorySection(0x00130000, 0x00150000, False, True), # Internal Memory Patchram Contents
MemorySection(0x00200000, 0x0023ffff, False, True), # Internal Memory Cortex M3
]
SECTIONS = [
MemorySection(0x00000000, 0x00100000, True, False), # Internal ROM
MemorySection(
0x00130000, 0x00150000, False, True
), # Internal Memory Patchram Contents
MemorySection(0x00200000, 0x0023FFFF, False, True), # Internal Memory Cortex M3
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x130000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
PATCHRAM_VALUE_TABLE_ADDRESS = 0x130000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
# Heap
BLOC_HEAD = 0x20067C # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
BLOC_HEAD = 0x20067C # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
+7 -7
View File
@@ -29,13 +29,13 @@ from .fw import MemorySection
FW_NAME = "BCM20702A2"
# Device Infos
#DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
#BD_ADDR = 0x280CA4 # rm_deviceBDAddr
# DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
# BD_ADDR = 0x280CA4 # rm_deviceBDAddr
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x5ffff, True, False), # Internal ROM
MemorySection(0x80000, 0x9bfff, False, True), # Internal RAM
]
BLOC_HEAD = 0x3166c
SECTIONS = [
MemorySection(0x00000000, 0x5FFFF, True, False), # Internal ROM
MemorySection(0x80000, 0x9BFFF, False, True), # Internal RAM
]
BLOC_HEAD = 0x3166C
+202 -186
View File
@@ -19,203 +19,219 @@
# liability, whether in an action of contract, tort or otherwise, arising from,
# out of or in connection with the Software or the use or other dealings in the
# Software.
from .fw import MemorySection, FirmwareDefinition
from .fw import MemorySection
# Firmware Infos
# Evaluation Kit CYW920735
FW_NAME = "CYW20735B1"
# Device Infos
DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
BD_ADDR = 0x280CA4 # rm_deviceBDAddr
#Heap
BLOC_HEAD = 0x200474 # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x001fffff, True, False), # Internal ROM
MemorySection(0x00200000, 0x0024ffff, False, True), # Internal Memory Cortex M3
MemorySection(0x00270000, 0x0027ffff, False, True), # Internal Memory Patchram Contents
MemorySection(0x00280000, 0x00283fff, False, True), # ToRam
MemorySection(0x00300000, 0x00307fff, False, True), # HW Regs Cortex M3 (readable)
MemorySection(0x00310000, 0x00321fff, False, True), # HW Regs Cortex M3 (readable)
MemorySection(0x00326000, 0x0032ffff, False, True), # HW Regs Cortex M3 (readable)
MemorySection(0x00338000, 0x00367fff, False, True), # HW Regs Cortex M3 (readable) + Pka Top
MemorySection(0x00370000, 0x0037ffff, False, True), # RTX FIFO
MemorySection(0x00390000, 0x00397fff, False, True), # Power WD
#MemorySection(0x00404000, 0x00407fff, False, True), # EF Registers (seem to be sometimes unavailable)
MemorySection(0x00410000, 0x00413fff, False, True), # BT Modem Registers
MemorySection(0x00420000, 0x00423fff, False, True), # FM Modem Registers
MemorySection(0x00430000, 0x00433fff, False, True), # MAC 15.4
MemorySection(0x00440000, 0x00443fff, False, True), # SecEng Top
MemorySection(0x00450000, 0x00453fff, False, True), # Capscan Top
MemorySection(0x00500000, 0x006007ff, False, True), # EPM RAM (readable) + RF Regs
MemorySection(0x00640000, 0x006407ff, False, True), # CLB Regs
MemorySection(0x00650000, 0x006507ff, False, True), # GCI Regs
MemorySection(0x20000000, 0x2024ffff, False, True), # SRAM
MemorySection(0x20270000, 0x20283fff, False, True), # SRAM
MemorySection(0x20500000, 0x200fffff, False, True), # SRAM
MemorySection(0x22000000, 0x2226ffff, False, True), # SRAM Bits?
MemorySection(0x40000000, 0x40003fff, False, True), # ToRam Alias / Peripherals
MemorySection(0x42000000, 0x4207ffff, False, True), # ToRam Bits
#MemorySection(0x60000000, 0x60000000, False, True), # Extern BlueRF SRAM (range TBD)
#MemorySection(0xa0000000, 0xa0000000, False, True), # Extern Device Address (range TBD)
MemorySection(0xe0000000, 0xe0100000, False, True), # Base PPB Address
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x270000
PATCHRAM_NUMBER_OF_SLOTS = 192
PATCHRAM_ALIGNED = False
# only seems to work 4-byte aligned here ...
from .. import Address
# Connection Struct and Table
CONNECTION_LIST_ADDRESS = 0x216F98 # pRm_whole_conn = 0x280C9C points to this
CONNECTION_MAX = 11 # g_bt_max_connections = 0 in firmware
CONNECTION_STRUCT_LENGTH = 0x168 # ??
class CYW20735B1(FirmwareDefinition):
FW_NAME = "CYW20735B1"
# Snippet for fuzzLmp()
FUZZLMP_HOOK_ADDRESS = 0xB08D8 # execute standard SendLmpPdu HCI to fill parameters
FUZZLMP_CODE_BASE_ADDRESS = 0x271A00 # memory area of other WICED patches
FUZZLMP_ASM_CODE = """
// This hook is put into the end of bthci_cmd_vs_SendLmpPdu_B08AC,
// so command parsing is still performed as normal. We jump in
// before bthci_cmd_vs_SendLmpPdu pops and calls DHM_LMPTx.
# Device Infos
DEVICE_NAME = (
0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
)
BD_ADDR = 0x280CA4 # rm_deviceBDAddr
mov r0, r6 // 4 byte alignment
# Heap
BLOC_HEAD = Address(0x200474) # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
// put length argument into table_entry
// payload[5] holds the size argument
ldr r5, =table_entry
add r5, #4 // length offset within table entry
ldrb r6, [r4, #5] // size is in position r4+5
strb r6, [r5]
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x001FFFFF, True, False), # Internal ROM
MemorySection(0x00200000, 0x0024FFFF, False, True), # Internal Memory Cortex M3
MemorySection(
0x00270000, 0x0027FFFF, False, True
), # Internal Memory Patchram Contents
MemorySection(0x00280000, 0x00283FFF, False, True), # ToRam
MemorySection(
0x00300000, 0x00307FFF, False, True
), # HW Regs Cortex M3 (readable)
MemorySection(
0x00310000, 0x00321FFF, False, True
), # HW Regs Cortex M3 (readable)
MemorySection(
0x00326000, 0x0032FFFF, False, True
), # HW Regs Cortex M3 (readable)
MemorySection(
0x00338000, 0x00367FFF, False, True
), # HW Regs Cortex M3 (readable) + Pka Top
MemorySection(0x00370000, 0x0037FFFF, False, True), # RTX FIFO
MemorySection(0x00390000, 0x00397FFF, False, True), # Power WD
# MemorySection(0x00404000, 0x00407fff, False, True), # EF Registers (seem to be sometimes unavailable)
MemorySection(0x00410000, 0x00413FFF, False, True), # BT Modem Registers
MemorySection(0x00420000, 0x00423FFF, False, True), # FM Modem Registers
MemorySection(0x00430000, 0x00433FFF, False, True), # MAC 15.4
MemorySection(0x00440000, 0x00443FFF, False, True), # SecEng Top
MemorySection(0x00450000, 0x00453FFF, False, True), # Capscan Top
MemorySection(
0x00500000, 0x006007FF, False, True
), # EPM RAM (readable) + RF Regs
MemorySection(0x00640000, 0x006407FF, False, True), # CLB Regs
MemorySection(0x00650000, 0x006507FF, False, True), # GCI Regs
MemorySection(0x20000000, 0x2024FFFF, False, True), # SRAM
MemorySection(0x20270000, 0x20283FFF, False, True), # SRAM
MemorySection(0x20500000, 0x200FFFFF, False, True), # SRAM
MemorySection(0x22000000, 0x2226FFFF, False, True), # SRAM Bits?
MemorySection(0x40000000, 0x40003FFF, False, True), # ToRam Alias / Peripherals
MemorySection(0x42000000, 0x4207FFFF, False, True), # ToRam Bits
# MemorySection(0x60000000, 0x60000000, False, True), # Extern BlueRF SRAM (range TBD)
# MemorySection(0xa0000000, 0xa0000000, False, True), # Extern Device Address (range TBD)
MemorySection(0xE0000000, 0xE0100000, False, True), # Base PPB Address
]
// we need to do the original pop...
pop {r4-r6, lr}
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = Address(0x310000)
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x270000
PATCHRAM_NUMBER_OF_SLOTS = 192
PATCHRAM_ALIGNED = False
# only seems to work 4-byte aligned here ...
// now we simply continue like the original DHM_LMPTx_3453E function
cmp r1, #0
itt eq
moveq r0, #4
bxeq lr
push {r4-r10, lr} // code at 0x34546
mov r7, r0
# Connection Struct and Table
CONNECTION_LIST_ADDRESS = 0x216F98 # pRm_whole_conn = 0x280C9C points to this
CONNECTION_MAX = 11 # g_bt_max_connections = 0 in firmware
CONNECTION_STRUCT_LENGTH = 0x168 # ??
// part of the check if hook_LMP_TxFilter is installed
ldr r0, =0x203144 //dhmAvLinkAutoDetectEnable
mov r4, r1
ldr r2, [r0, #12]
//cbz r2, loc_34564
# Snippet for fuzzLmp()
FUZZLMP_HOOK_ADDRESS = 0xB08D8 # execute standard SendLmpPdu HCI to fill parameters
FUZZLMP_CODE_BASE_ADDRESS = 0x271A00 # memory area of other WICED patches
FUZZLMP_ASM_CODE = """
// This hook is put into the end of bthci_cmd_vs_SendLmpPdu_B08AC,
// so command parsing is still performed as normal. We jump in
// before bthci_cmd_vs_SendLmpPdu pops and calls DHM_LMPTx.
mov r0, r6 // 4 byte alignment
// put length argument into table_entry
// payload[5] holds the size argument
ldr r5, =table_entry
add r5, #4 // length offset within table entry
ldrb r6, [r4, #5] // size is in position r4+5
strb r6, [r5]
// we need to do the original pop...
pop {r4-r6, lr}
// now we simply continue like the original DHM_LMPTx_3453E function
cmp r1, #0
itt eq
moveq r0, #4
bxeq lr
push {r4-r10, lr} // code at 0x34546
mov r7, r0
// part of the check if hook_LMP_TxFilter is installed
ldr r0, =0x203144 //dhmAvLinkAutoDetectEnable
mov r4, r1
ldr r2, [r0, #12]
//cbz r2, loc_34564
ldr.w r8, [r7] // code at 0x34564
mov r0, r8
bl 0x93E60 // rm_getDHMAclPtr
movs r5, r0
// skip check if we actually got a ptr
// continue at 0x3457A
ldrb r0, [r4, #12]
tst.w r0, #0xfe // test for extended op ...
add.w r0, r3, #0xc
// now we regularily would call the opcode conversion table function
// however, we do not use lm_getLmpInfoType_86A82 but insert our own table here
ldr r1, =table_entry // table_ptr with exactly one entry, so no offsets included here
ldr r0, =table_entry
// branch back to DHM_LMPTx position after bl lm_getLmpInfoType
b 0x3458A
.align
table_entry:
.byte 0x40 //lm_LmpUnsupportedPdu
.byte 0x6A
.byte 0x08
.byte 0x00
.byte 0x20 //length, will be overwritten by us anyways, but can not be longer than one buffer (0x20)
.byte 0x00
.byte 0x00
.byte 0x00
"""
ldr.w r8, [r7] // code at 0x34564
mov r0, r8
bl 0x93E60 // rm_getDHMAclPtr
movs r5, r0
// skip check if we actually got a ptr
// continue at 0x3457A
ldrb r0, [r4, #12]
tst.w r0, #0xfe // test for extended op ...
add.w r0, r3, #0xc
# Assembler snippet for tracepoints
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
TRACEPOINT_BODY_ASM_LOCATION = 0x00218500
TRACEPOINT_HOOKS_LOCATION = 0x00218700
TRACEPOINT_HOOK_SIZE = 40
TRACEPOINT_HOOK_ASM = """
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
mov r0, %d // addTracepoint() injects the patchram slot of the hook patch
bl 0x28794 // patch_uninstallPatchEntry(slot)
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
pop {r0-r12, lr} // restore registers
// branch back to the original instruction
b 0x%x // addTracepoint() injects the address of the tracepoint
"""
// now we regularily would call the opcode conversion table function
// however, we do not use lm_getLmpInfoType_86A82 but insert our own table here
ldr r1, =table_entry // table_ptr with exactly one entry, so no offsets included here
ldr r0, =table_entry
// branch back to DHM_LMPTx position after bl lm_getLmpInfoType
b 0x3458A
.align
table_entry:
.byte 0x40 //lm_LmpUnsupportedPdu
.byte 0x6A
.byte 0x08
.byte 0x00
.byte 0x20 //length, will be overwritten by us anyways, but can not be longer than one buffer (0x20)
.byte 0x00
.byte 0x00
.byte 0x00
"""
# Assembler snippet for tracepoints
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
TRACEPOINT_BODY_ASM_LOCATION = 0x00218500
TRACEPOINT_HOOKS_LOCATION = 0x00218700
TRACEPOINT_HOOK_SIZE = 40
TRACEPOINT_HOOK_ASM = """
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
mov r0, %d // addTracepoint() injects the patchram slot of the hook patch
bl 0x28794 // patch_uninstallPatchEntry(slot)
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
pop {r0-r12, lr} // restore registers
// branch back to the original instruction
b 0x%x // addTracepoint() injects the address of the tracepoint
"""
TRACEPOINT_BODY_ASM_SNIPPET = """
mov r8, lr // save link register in r8
// dump registers like before
// save status register in r5
mrs r5, cpsr
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
bl 0x2DEF4 // hci_allocateEventBlockWithLen(0xff, 78)
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'TRACE_') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x43415254 // 'TRAC'
str r1, [r0]
add r0, 4 // advance the pointer.
ldr r1, =0x5f45 // 'E_'
strh r1, [r0]
add r0, 2 // advance the pointer. r0 now points to the start of the register values
// store pc
str r6, [r0] // r6 still contains the address of the original pc
add r0, 4 // advance the pointer.
// store sp
mov r1, 56 // 14 saved registers * 4
add r1, sp
str r1, [r0]
add r0, 4 // advance the pointer.
// store status register
str r5, [r0]
add r0, 4 // advance the pointer.
// store other registers
mov r1, sp
mov r2, 56
bl 0xEAB4 // memcpy(dst, src, len)
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0x2DEC0 // hci_sendEvent
// restore status register
msr cpsr_f, r5
bl 0x26C7A // bthci_event_vs_DBFW_CoreDumpRAMImageEvent
mov lr, r8 // restore lr from r8
bx lr // return
"""
TRACEPOINT_BODY_ASM_SNIPPET = """
mov r8, lr // save link register in r8
// dump registers like before
// save status register in r5
mrs r5, cpsr
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
bl 0x2DEF4 // hci_allocateEventBlockWithLen(0xff, 78)
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'TRACE_') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x43415254 // 'TRAC'
str r1, [r0]
add r0, 4 // advance the pointer.
ldr r1, =0x5f45 // 'E_'
strh r1, [r0]
add r0, 2 // advance the pointer. r0 now points to the start of the register values
// store pc
str r6, [r0] // r6 still contains the address of the original pc
add r0, 4 // advance the pointer.
// store sp
mov r1, 56 // 14 saved registers * 4
add r1, sp
str r1, [r0]
add r0, 4 // advance the pointer.
// store status register
str r5, [r0]
add r0, 4 // advance the pointer.
// store other registers
mov r1, sp
mov r2, 56
bl 0xEAB4 // memcpy(dst, src, len)
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0x2DEC0 // hci_sendEvent
// restore status register
msr cpsr_f, r5
bl 0x26C7A // bthci_event_vs_DBFW_CoreDumpRAMImageEvent
mov lr, r8 // restore lr from r8
bx lr // return
"""
+103 -98
View File
@@ -19,110 +19,115 @@
# liability, whether in an action of contract, tort or otherwise, arising from,
# out of or in connection with the Software or the use or other dealings in the
# Software.
from .fw import MemorySection, FirmwareDefinition
from .fw import MemorySection
# Firmware Infos
# Evaluation Kit CYW920719
FW_NAME = "CYW20739B1 (NOT iPhone X/XR!)"
# TODO this is not the iPhone firmware, we need to add a switch in fw.py
# Device Infos
DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
BD_ADDR = 0x280CA4 # rm_deviceBDAddr
#Heap
BLOC_HEAD = 0x0200c7c # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x001fffff, True, False), # Internal ROM
MemorySection(0x00200000, 0x0024ffff, False, True), # Internal Memory Cortex M3
MemorySection(0x00270000, 0x0027ffff, False, True), # Internal Memory Patchram Contents
MemorySection(0x00280000, 0x00283fff, False, True), # ToRam
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x270000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
# only seems to work 4-byte aligned here ...
from .. import Address
# Assembler snippet for tracepoints
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
TRACEPOINT_BODY_ASM_LOCATION = 0x00223100
TRACEPOINT_HOOKS_LOCATION = 0x00223200
TRACEPOINT_HOOK_SIZE = 40
TRACEPOINT_HOOK_ASM = """
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
mov r0, %d // addTracepoint() injects the patchram slot of the hook patch
bl 0x34964 // patch_uninstallPatchEntry(slot)
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
pop {r0-r12, lr} // restore registers
class CYW20739B1(FirmwareDefinition):
# Firmware Infos
# Evaluation Kit CYW920719
FW_NAME = "CYW20739B1 (NOT iPhone X/XR!)"
# TODO this is not the iPhone firmware, we need to add a switch in fw.py
// branch back to the original instruction
b 0x%x // addTracepoint() injects the address of the tracepoint
"""
# Device Infos
DEVICE_NAME = (
0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
)
BD_ADDR = 0x280CA4 # rm_deviceBDAddr
TRACEPOINT_BODY_ASM_SNIPPET = """
mov r8, lr // save link register in r8
# Heap
BLOC_HEAD = Address(0x0200C7C) # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
// dump registers like before
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x001FFFFF, True, False), # Internal ROM
MemorySection(0x00200000, 0x0024FFFF, False, True), # Internal Memory Cortex M3
MemorySection(
0x00270000, 0x0027FFFF, False, True
), # Internal Memory Patchram Contents
MemorySection(0x00280000, 0x00283FFF, False, True), # ToRam
]
// save status register in r5
mrs r5, cpsr
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
bl 0xF7B6 // hci_allocateEventBlockWithLen(0xff, 78)
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'TRACE_') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x43415254 // 'TRAC'
str r1, [r0]
add r0, 4 // advance the pointer.
ldr r1, =0x5f45 // 'E_'
strh r1, [r0]
add r0, 2 // advance the pointer. r0 now points to the start of the register values
// store pc
str r6, [r0] // r6 still contains the address of the original pc
add r0, 4 // advance the pointer.
// store sp
mov r1, 56 // 14 saved registers * 4
add r1, sp
str r1, [r0]
add r0, 4 // advance the pointer.
// store status register
str r5, [r0]
add r0, 4 // advance the pointer.
// store other registers
mov r1, sp
mov r2, 56
bl 0xAF0BC // memcpy(dst, src, len)
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0xF782 // hci_sendEvent
// restore status register
msr cpsr_f, r5
bl 0x2D702 // bthci_event_vs_DBFW_CoreDumpRAMImageEvent
mov lr, r8 // restore lr from r8
bx lr // return
"""
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = Address(0x310000)
PATCHRAM_ENABLED_BITMAP_ADDRESS = Address(0x310404)
PATCHRAM_VALUE_TABLE_ADDRESS = Address(0x270000)
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
# only seems to work 4-byte aligned here ...
# Assembler snippet for tracepoints
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
TRACEPOINT_BODY_ASM_LOCATION = 0x00223100
TRACEPOINT_HOOKS_LOCATION = 0x00223200
TRACEPOINT_HOOK_SIZE = 40
TRACEPOINT_HOOK_ASM = """
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
mov r0, %d // addTracepoint() injects the patchram slot of the hook patch
bl 0x34964 // patch_uninstallPatchEntry(slot)
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
pop {r0-r12, lr} // restore registers
// branch back to the original instruction
b 0x%x // addTracepoint() injects the address of the tracepoint
"""
TRACEPOINT_BODY_ASM_SNIPPET = """
mov r8, lr // save link register in r8
// dump registers like before
// save status register in r5
mrs r5, cpsr
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
bl 0xF7B6 // hci_allocateEventBlockWithLen(0xff, 78)
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'TRACE_') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x43415254 // 'TRAC'
str r1, [r0]
add r0, 4 // advance the pointer.
ldr r1, =0x5f45 // 'E_'
strh r1, [r0]
add r0, 2 // advance the pointer. r0 now points to the start of the register values
// store pc
str r6, [r0] // r6 still contains the address of the original pc
add r0, 4 // advance the pointer.
// store sp
mov r1, 56 // 14 saved registers * 4
add r1, sp
str r1, [r0]
add r0, 4 // advance the pointer.
// store status register
str r5, [r0]
add r0, 4 // advance the pointer.
// store other registers
mov r1, sp
mov r2, 56
bl 0xAF0BC // memcpy(dst, src, len)
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0xF782 // hci_sendEvent
// restore status register
msr cpsr_f, r5
bl 0x2D702 // bthci_event_vs_DBFW_CoreDumpRAMImageEvent
mov lr, r8 // restore lr from r8
bx lr // return
"""
+11 -8
View File
@@ -28,14 +28,17 @@ FW_NAME = "BCM4347B1"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x00103fff, True, False), # Internal ROM
MemorySection(0x00130000, 0x0014ffff, False, True), # Internal Memory Patchram Contents
MemorySection(0x00200000, 0x0024ffff, False, True), # Internal Memory Cortex M3
]
SECTIONS = [
MemorySection(0x00000000, 0x00103FFF, True, False), # Internal ROM
MemorySection(
0x00130000, 0x0014FFFF, False, True
), # Internal Memory Patchram Contents
MemorySection(0x00200000, 0x0024FFFF, False, True), # Internal Memory Cortex M3
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x130000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
PATCHRAM_VALUE_TABLE_ADDRESS = 0x130000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
+5 -4
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python2
# MacBook 15" early 2011 tested with Ubuntu
# MacBook 15" early 2011 tested with Ubuntu
#
# Generic firmware file in case we do not know something...
#
@@ -30,8 +30,9 @@ FW_NAME = "BCM2070B0 (MacBook Pro 2011)"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x0, 0x58000, True , False),
MemorySection(0x80000, 0x9b000, False, True ),
]
SECTIONS = [
MemorySection(0x0, 0x58000, True, False),
MemorySection(0x80000, 0x9B000, False, True),
]
BLOC_HEAD = 0x88518
+21 -20
View File
@@ -27,28 +27,29 @@ from .fw import MemorySection
FW_NAME = "BCM4355C0"
# Device Infos
DEVICE_NAME = 0x204c60
DEVICE_NAME = 0x204C60
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x0, 0x90000, True , False),
MemorySection(0xd0000, 0xd8000, False, True ),
#MemorySection(0xe0000, 0x1f0000, True , False),
MemorySection(0x200000, 0x228000, False, True ),
MemorySection(0x260000, 0x268000, True , False),
#MemorySection(0x280000, 0x2a0000, True , False),
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x360000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
#MemorySection(0x680000, 0x800000, False, False)
]
SECTIONS = [
MemorySection(0x0, 0x90000, True, False),
MemorySection(0xD0000, 0xD8000, False, True),
# MemorySection(0xe0000, 0x1f0000, True , False),
MemorySection(0x200000, 0x228000, False, True),
MemorySection(0x260000, 0x268000, True, False),
# MemorySection(0x280000, 0x2a0000, True , False),
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x360000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
# MemorySection(0x680000, 0x800000, False, False)
]
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
PATCHRAM_NUMBER_OF_SLOTS = 192
PATCHRAM_ALIGNED = False
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
PATCHRAM_NUMBER_OF_SLOTS = 192
PATCHRAM_ALIGNED = False
+359 -342
View File
@@ -24,363 +24,380 @@
# liability, whether in an action of contract, tort or otherwise, arising from,
# out of or in connection with the Software or the use or other dealings in the
# Software.
from .fw import MemorySection
# Firmware Infos
# This runs on Nexus 5, Xperia Z3, Samsung Galaxy Note 3
FW_NAME = "BCM4335C0"
# Device Infos
DEVICE_NAME = 0x2178B4 # [type: 1byte] [len: 1byte] [name: len byte]
BD_ADDR = 0x210C2C
from .fw import MemorySection, FirmwareDefinition
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x0, 0x90000, True , False),
MemorySection(0xd0000, 0xd8000, False, True ),
#MemorySection(0xe0000, 0x1f0000, True , False),
MemorySection(0x200000, 0x228000, False, True ),
MemorySection(0x260000, 0x268000, True , False),
#MemorySection(0x280000, 0x2a0000, True , False),
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x360000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
#MemorySection(0x680000, 0x800000, False, False)
]
class BCM4335C0(FirmwareDefinition):
# Firmware Infos
# This runs on Nexus 5, Xperia Z3, Samsung Galaxy Note 3
FW_NAME = "BCM4335C0"
# BLOC struct head which points to the first bloc struct (double-linked list)
BLOC_HEAD = 0x203094
# Device Infos
DEVICE_NAME = 0x2178B4 # [type: 1byte] [len: 1byte] [name: len byte]
BD_ADDR = 0x210C2C
# QUEU struct head which points to the first queue struct (double-linked list)
QUEUE_HEAD = 0x20307C
QUEUE_NAMES = ["tran_HCIEvent", "tran_ACLData", "tran_SCOData", "tran_UartBridgeNonHCIEvent", "tran_DiagData",
"tran_HIDUsbKBEvt", "tran_HIDUsbMSEvt", "tran_HIDUsbMSCtrl", "tran_HIDUsbKBCtrl", "tran_HidAuxData",
"lm_Cmd", "hci_HciCommand", "lm_deferredAction", "lrmmsm_cmd", "liteHostEvent", "litehostRcvdL2capData"
]
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x0, 0x90000, True, False),
MemorySection(0xD0000, 0xD8000, False, True),
# MemorySection(0xe0000, 0x1f0000, True , False),
MemorySection(0x200000, 0x228000, False, True),
MemorySection(0x260000, 0x268000, True, False),
# MemorySection(0x280000, 0x2a0000, True , False),
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x360000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
# MemorySection(0x680000, 0x800000, False, False)
]
# BLOC struct head which points to the first bloc struct (double-linked list)
BLOC_HEAD = 0x203094
# Connection Structure and Table
CONNECTION_ARRAY_ADDRESS = 0x002038E8
CONNECTION_MAX = 11
CONNECTION_STRUCT_LENGTH = 0x14C
# QUEU struct head which points to the first queue struct (double-linked list)
QUEUE_HEAD = 0x20307C
QUEUE_NAMES = [
"tran_HCIEvent",
"tran_ACLData",
"tran_SCOData",
"tran_UartBridgeNonHCIEvent",
"tran_DiagData",
"tran_HIDUsbKBEvt",
"tran_HIDUsbMSEvt",
"tran_HIDUsbMSCtrl",
"tran_HIDUsbKBCtrl",
"tran_HidAuxData",
"lm_Cmd",
"hci_HciCommand",
"lm_deferredAction",
"lrmmsm_cmd",
"liteHostEvent",
"litehostRcvdL2capData",
]
# Connection Structure and Table
CONNECTION_ARRAY_ADDRESS = 0x002038E8
CONNECTION_MAX = 11
CONNECTION_STRUCT_LENGTH = 0x14C
# Patchram
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = True #use readMemAligned, not accessible via ReadRAM HCI command on Nexus 5
# Patchram
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = (
True # use readMemAligned, not accessible via ReadRAM HCI command on Nexus 5
)
# Snippet for sendLcpPacket()
SENDLCP_CODE_BASE_ADDRESS = 0x00211900
SENDLCP_ASM_CODE = """
push {r4,lr}
// we want to call lmulp_sendLcp(conn_index, input, length)
mov r0, %d // connection index, starts at 0
ldr r1, =payload
mov r2, %d // length
bl 0x66760 // lmulp_sendLcp
pop {r4,pc} // go back
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
payload: // Note: the payload will be appended here by the sendLmpPacket() function
"""
# Snippet for sendLcpPacket()
SENDLCP_CODE_BASE_ADDRESS = 0x00211900
SENDLCP_ASM_CODE = """
push {r4,lr}
// we want to call lmulp_sendLcp(conn_index, input, length)
mov r0, %d // connection index, starts at 0
ldr r1, =payload
mov r2, %d // length
bl 0x66760 // lmulp_sendLcp
pop {r4,pc} // go back
# Snippet for sendLmpPacketLegacy()
SENDLMP_CODE_BASE_ADDRESS = 0xD7500
SENDLMP_ASM_CODE = """
push {r4,lr}
// malloc buffer for LMP packet
bl 0x3F17E // malloc_0x20_bloc_buffer_memzero
mov r4, r0 // store buffer for LMP packet inside r4
// fill buffer
add r0, 0xC // The actual LMP packet must start at offset 0xC in the buffer.
// The first 12 bytes are (supposely?) unused and remain zero.
ldr r1, =payload // LMP packet is stored at the end of the snippet
mov r2, 20 // Max. size of an LMP packet is 19 (I guess). The send_LMP_packet
// function will use the LMP opcode to lookup the actual size and
// use it for actually transmitting the correct number of bytes.
bl 0x2e03c // memcpy
// load conn struct pointer (needed for determine if we are master or slave)
mov r0, %d // connection number is injected by sendLmpPacket()
bl 0x42c04 // find connection struct from conn nr (r0 will hold pointer to conn struct)
// set tid bit if we are the slave
ldr r1, [r0, 0x1c] // Load a bitmap from the connection struct into r1.
lsr r1, 15 // The 'we are master'-bit is at position 15 of this bitmap
and r1, 0x1 // isolate the bit to get the correct value for the TID bit
ldr r2, [r4, 0xC] // Load the LMP opcode into r2. Note: The opcode was already shifted
// left by 1 bit (done by sendLmpPacket()). The TID bit goes into
// the LSB (least significant bit) of this shifted opcode byte.
orr r2, r1 // insert the TID bit into the byte
str r2, [r4, 0xC] // Store the byte back into the LMP packet buffer
// send LMP packet
mov r1, r4 // load the address of the LMP packet buffer into r1.
// r0 still contains the connection number.
pop {r4,lr} // restore r4 and the lr
b 0xf81a // branch to send_LMP_packet. send_LMP_packet will do the return for us.
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
payload: // Note: the payload will be appended here by the sendLmpPacket() function
"""
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
payload: // Note: the payload will be appended here by the sendLmpPacket() function
# Snippet for fuzzLmp()
FUZZLMP_HOOK_ADDRESS = 0x1E48C # execute standard SendLmpPdu HCI to fill parameters
FUZZLMP_CODE_BASE_ADDRESS = 0xD7500
FUZZLMP_ASM_CODE = """
// This hook is put into the end of bthci_cmd_vs_SendLmpPdu_1E45A,
// so command parsing is still performed as normal. We jump in
// before bthci_cmd_vs_SendLmpPdu pops and calls DHM_LMPTx.
// put length argument into table_entry
// payload[5] holds the size argument
ldr r7, =table_entry
add r7, #4 // length offset within table entry
ldrb r6, [r4, #5]
strb r6, [r7]
// we need to do the original pop...
pop {r4-r8, lr}
// now we simply continue like the original DHM_LMPTx function
push {r4-r10, lr} // code at 0xF81A
mov r8, r0
movs r4, r1
// part of the check if hook_LMP_TxFilter_200D38 is installed
ldr.w r9, =0x200D80
sub.w r9, r9, #0x50
ldr.w r2, [r9, #8]
mov r1, r4 // code at 0xF840
mov r0, r8
bl 0x50050 // rm_getDHMAclPtr_50050
cmp r0, #1
// skip check if we actually got a ptr
// get connection struct, code at 0xF850
ldr r0, =0x206eac // table_for_bt_tx_structs_206EAC
ldr.w r7, [r8]
add.w r6, r0, r7, lsl #5
ldrb r0, [r6, #0x1f]
cmp r0, #1
// skip check in conection struct
ldrb r0, [r4, #0xc] // ptr_to_opcode = buffer_cpy + 12;
lsrs r1, r0, #1 // buffer_cpy[12] >> 1
add.w r0, r4, #0x0c
// enable for debugging: ignore the remaining code and continue like normal opcode
//b 0xF870
// now we regularily would call the opcode conversion table function
// however, we do not use lm_getLmpInfoType_3F2D8 but insert our own table here
ldr r1, =table_entry // table_ptr with exactly one entry, so no offsets included here
ldr r0, =table_entry
// branch back to DHM_LMPTx position after bl lm_getLmpInfoType
b 0xF874
.align
table_entry:
.byte 0x35 //nullsub1+1
.byte 0xAC
.byte 0x00
.byte 0x00
.byte 0x20 //length, will be overwritten by us anyways, but can not be longer than one buffer (0x20)
.byte 0x00
.byte 0x00
.byte 0x00
"""
# Assembler snippet for the readMemAligned() function
READ_MEM_ALIGNED_ASM_LOCATION = 0xD7900
READ_MEM_ALIGNED_ASM_SNIPPET = """
push {r4, lr}
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, %d // readMemAligned() injects the number of bytes it wants to read here
add r1, 6 // + type and length + 'READ'
bl 0x7AFC // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'READ') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x44414552 // 'READ'
str r1, [r0]
add r0, 4 // advance the pointer. r0 now points to the beginning of our read data
// copy data to buffer
ldr r1, =0x%x // readMemAligned() injects the read_address here. r1 will be used as src pointer in the loop
mov r2, %d // readMemAligned() injects the number of dwords to read here. r2 will be the loop counter
loop:
ldr r3, [r1] // read 4 bytes from the read_address
str r3, [r0] // store them inside the HCI buffer
add r0, 4 // advance the buffer pointer
add r1, 4 // advance the read_address
subs r2, 1 // decrement the loop variable
bne loop // branch if r2 is not zero yet
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0x398c1 // send_hci_event_without_free()
// free HCI buffer
mov r0, r4
bl 0x3FA36 // free_bloc_buffer_aligned
pop {r4, pc} // return
"""
# Snippet for sendLmpPacketLegacy()
SENDLMP_CODE_BASE_ADDRESS = 0xd7500
SENDLMP_ASM_CODE = """
push {r4,lr}
// malloc buffer for LMP packet
bl 0x3F17E // malloc_0x20_bloc_buffer_memzero
mov r4, r0 // store buffer for LMP packet inside r4
// fill buffer
add r0, 0xC // The actual LMP packet must start at offset 0xC in the buffer.
// The first 12 bytes are (supposely?) unused and remain zero.
ldr r1, =payload // LMP packet is stored at the end of the snippet
mov r2, 20 // Max. size of an LMP packet is 19 (I guess). The send_LMP_packet
// function will use the LMP opcode to lookup the actual size and
// use it for actually transmitting the correct number of bytes.
bl 0x2e03c // memcpy
// load conn struct pointer (needed for determine if we are master or slave)
mov r0, %d // connection number is injected by sendLmpPacket()
bl 0x42c04 // find connection struct from conn nr (r0 will hold pointer to conn struct)
// set tid bit if we are the slave
ldr r1, [r0, 0x1c] // Load a bitmap from the connection struct into r1.
lsr r1, 15 // The 'we are master'-bit is at position 15 of this bitmap
and r1, 0x1 // isolate the bit to get the correct value for the TID bit
ldr r2, [r4, 0xC] // Load the LMP opcode into r2. Note: The opcode was already shifted
// left by 1 bit (done by sendLmpPacket()). The TID bit goes into
// the LSB (least significant bit) of this shifted opcode byte.
orr r2, r1 // insert the TID bit into the byte
str r2, [r4, 0xC] // Store the byte back into the LMP packet buffer
// send LMP packet
mov r1, r4 // load the address of the LMP packet buffer into r1.
// r0 still contains the connection number.
pop {r4,lr} // restore r4 and the lr
b 0xf81a // branch to send_LMP_packet. send_LMP_packet will do the return for us.
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
payload: // Note: the payload will be appended here by the sendLmpPacket() function
"""
# Snippet for fuzzLmp()
FUZZLMP_HOOK_ADDRESS = 0x1e48c # execute standard SendLmpPdu HCI to fill parameters
FUZZLMP_CODE_BASE_ADDRESS = 0xd7500
FUZZLMP_ASM_CODE = """
// This hook is put into the end of bthci_cmd_vs_SendLmpPdu_1E45A,
// so command parsing is still performed as normal. We jump in
// before bthci_cmd_vs_SendLmpPdu pops and calls DHM_LMPTx.
// put length argument into table_entry
// payload[5] holds the size argument
ldr r7, =table_entry
add r7, #4 // length offset within table entry
ldrb r6, [r4, #5]
strb r6, [r7]
// we need to do the original pop...
pop {r4-r8, lr}
// now we simply continue like the original DHM_LMPTx function
push {r4-r10, lr} // code at 0xF81A
mov r8, r0
movs r4, r1
// part of the check if hook_LMP_TxFilter_200D38 is installed
ldr.w r9, =0x200D80
sub.w r9, r9, #0x50
ldr.w r2, [r9, #8]
mov r1, r4 // code at 0xF840
mov r0, r8
bl 0x50050 // rm_getDHMAclPtr_50050
cmp r0, #1
// skip check if we actually got a ptr
// get connection struct, code at 0xF850
ldr r0, =0x206eac // table_for_bt_tx_structs_206EAC
ldr.w r7, [r8]
add.w r6, r0, r7, lsl #5
ldrb r0, [r6, #0x1f]
cmp r0, #1
// skip check in conection struct
ldrb r0, [r4, #0xc] // ptr_to_opcode = buffer_cpy + 12;
lsrs r1, r0, #1 // buffer_cpy[12] >> 1
add.w r0, r4, #0x0c
// enable for debugging: ignore the remaining code and continue like normal opcode
//b 0xF870
// now we regularily would call the opcode conversion table function
// however, we do not use lm_getLmpInfoType_3F2D8 but insert our own table here
ldr r1, =table_entry // table_ptr with exactly one entry, so no offsets included here
ldr r0, =table_entry
// branch back to DHM_LMPTx position after bl lm_getLmpInfoType
b 0xF874
.align
table_entry:
.byte 0x35 //nullsub1+1
.byte 0xAC
.byte 0x00
.byte 0x00
.byte 0x20 //length, will be overwritten by us anyways, but can not be longer than one buffer (0x20)
.byte 0x00
.byte 0x00
.byte 0x00
"""
# Assembler snippet for the readMemAligned() function
READ_MEM_ALIGNED_ASM_LOCATION = 0xd7900
READ_MEM_ALIGNED_ASM_SNIPPET = """
push {r4, lr}
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, %d // readMemAligned() injects the number of bytes it wants to read here
add r1, 6 // + type and length + 'READ'
bl 0x7AFC // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'READ') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x44414552 // 'READ'
str r1, [r0]
add r0, 4 // advance the pointer. r0 now points to the beginning of our read data
// copy data to buffer
ldr r1, =0x%x // readMemAligned() injects the read_address here. r1 will be used as src pointer in the loop
mov r2, %d // readMemAligned() injects the number of dwords to read here. r2 will be the loop counter
loop:
ldr r3, [r1] // read 4 bytes from the read_address
str r3, [r0] // store them inside the HCI buffer
add r0, 4 // advance the buffer pointer
add r1, 4 // advance the read_address
subs r2, 1 // decrement the loop variable
bne loop // branch if r2 is not zero yet
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0x398c1 // send_hci_event_without_free()
// free HCI buffer
mov r0, r4
bl 0x3FA36 // free_bloc_buffer_aligned
pop {r4, pc} // return
# Assembler snippet for tracepoints
TRACEPOINT_BODY_ASM_LOCATION = 0xD7A00
TRACEPOINT_HOOKS_LOCATION = 0xD7B00
TRACEPOINT_HOOK_SIZE = 28
TRACEPOINT_HOOK_ASM = """
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
mov r7, %d // addTracepoint() injects the patchram slot of the hook patch
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
pop {r0-r12, lr} // restore registers
// branch back to the original instruction
b 0x%x // addTracepoint() injects the address of the tracepoint
"""
# Assembler snippet for tracepoints
TRACEPOINT_BODY_ASM_LOCATION = 0xd7a00
TRACEPOINT_HOOKS_LOCATION = 0xd7b00
TRACEPOINT_HOOK_SIZE = 28
TRACEPOINT_HOOK_ASM = """
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
mov r7, %d // addTracepoint() injects the patchram slot of the hook patch
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
pop {r0-r12, lr} // restore registers
// branch back to the original instruction
b 0x%x // addTracepoint() injects the address of the tracepoint
"""
TRACEPOINT_RAM_DUMP_PKT_COUNT = 670 # <ramsize> / <packetsize> where packetsize is 244
TRACEPOINT_BODY_ASM_SNIPPET = """
mov r8, lr // save link register in r8
// save status register in r5
mrs r5, cpsr
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
bl 0x7AFC // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'TRACE_') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x43415254 // 'TRAC'
str r1, [r0]
add r0, 4 // advance the pointer.
ldr r1, =0x5f45 // 'E_'
strh r1, [r0]
add r0, 2 // advance the pointer. r0 now points to the start of the register values
// store pc
str r6, [r0] // r6 still contains the address of the original pc
add r0, 4 // advance the pointer.
// store sp
mov r1, 56 // 14 saved registers * 4
add r1, sp
str r1, [r0]
add r0, 4 // advance the pointer.
// store status register
str r5, [r0]
add r0, 4 // advance the pointer.
// store other registers
mov r1, sp
mov r2, 56
bl 0x2e03c+1 // memcpy(dst, src, len)
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0x398c1 // send_hci_event_without_free()
// free HCI buffer
mov r0, r4
bl 0x3FA36 // free_bloc_buffer_aligned
mov r0, r7 // r7 still contains the patchram slot number
bl 0x311AA // disable_patchram_slot(slot)
// restore status register
msr cpsr_f, r5
// dump ram
bl dump_ram
mov lr, r8 // restore lr from r8
bx lr // return
// function to dump the RAM as multiple HCI packets:
dump_ram:
push {r4-r6,lr}
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, 252 // buffer size
bl 0x7AFC // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'RAM___') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x5f4d4152 // 'RAM_'
str r1, [r0]
add r0, 4 // advance the pointer.
ldr r1, =0x5f5f // '__'
strh r1, [r0]
add r0, 2 // advance the pointer. r0 now points to the start of the actual payload
mov r5, 0x200000 // start of ram
ldr r6, =%d // number of ramdump packets to be sent
dump_ram_loop:
// Set r0 to point to the beginning of the payload in the hci buffer
mov r0, r4
add r0, 8
// store current address
str r5, [r0] // r5 contains the address in RAM which is send next
TRACEPOINT_RAM_DUMP_PKT_COUNT = (
670 # <ramsize> / <packetsize> where packetsize is 244
)
TRACEPOINT_BODY_ASM_SNIPPET = (
"""
mov r8, lr // save link register in r8
// save status register in r5
mrs r5, cpsr
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
bl 0x7AFC // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'TRACE_') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x43415254 // 'TRAC'
str r1, [r0]
add r0, 4 // advance the pointer.
ldr r1, =0x5f45 // 'E_'
strh r1, [r0]
add r0, 2 // advance the pointer. r0 now points to the start of the register values
// store pc
str r6, [r0] // r6 still contains the address of the original pc
add r0, 4 // advance the pointer.
// copy ram to hci buffer
mov r1, r5
mov r2, 244
bl 0x2e03c // memcpy
// store sp
mov r1, 56 // 14 saved registers * 4
add r1, sp
str r1, [r0]
add r0, 4 // advance the pointer.
// store status register
str r5, [r0]
add r0, 4 // advance the pointer.
// store other registers
mov r1, sp
mov r2, 56
bl 0x2e03c+1 // memcpy(dst, src, len)
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0x398c1 // send_hci_event_without_free()
// delay loop; Workaround: without the delay, a lot of packets are not actually sent
// through HCI.
mov r0, 0x1000
delay_loop:
subs r0, 1
bne delay_loop
// increment the RAM pointer; decrement the counter
add r5, 244
subs r6, 1
bne dump_ram_loop
// free HCI buffer
mov r0, r4
bl 0x3FA36 // free_bloc_buffer_aligned
pop {r4-r6,pc}
""" % TRACEPOINT_RAM_DUMP_PKT_COUNT
// free HCI buffer
mov r0, r4
bl 0x3FA36 // free_bloc_buffer_aligned
mov r0, r7 // r7 still contains the patchram slot number
bl 0x311AA // disable_patchram_slot(slot)
// restore status register
msr cpsr_f, r5
// dump ram
bl dump_ram
mov lr, r8 // restore lr from r8
bx lr // return
// function to dump the RAM as multiple HCI packets:
dump_ram:
push {r4-r6,lr}
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, 252 // buffer size
bl 0x7AFC // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'RAM___') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x5f4d4152 // 'RAM_'
str r1, [r0]
add r0, 4 // advance the pointer.
ldr r1, =0x5f5f // '__'
strh r1, [r0]
add r0, 2 // advance the pointer. r0 now points to the start of the actual payload
mov r5, 0x200000 // start of ram
ldr r6, =%d // number of ramdump packets to be sent
dump_ram_loop:
// Set r0 to point to the beginning of the payload in the hci buffer
mov r0, r4
add r0, 8
// store current address
str r5, [r0] // r5 contains the address in RAM which is send next
add r0, 4 // advance the pointer.
// copy ram to hci buffer
mov r1, r5
mov r2, 244
bl 0x2e03c // memcpy
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0x398c1 // send_hci_event_without_free()
// delay loop; Workaround: without the delay, a lot of packets are not actually sent
// through HCI.
mov r0, 0x1000
delay_loop:
subs r0, 1
bne delay_loop
// increment the RAM pointer; decrement the counter
add r5, 244
subs r6, 1
bne dump_ram_loop
// free HCI buffer
mov r0, r4
bl 0x3FA36 // free_bloc_buffer_aligned
pop {r4-r6,pc}
"""
% TRACEPOINT_RAM_DUMP_PKT_COUNT
)
+27 -24
View File
@@ -28,6 +28,7 @@
# Firmware Infos
# This runs on Rasperry Pi 3+
from builtins import object
FW_NAME = "BCM4345C0"
# Device Infos
@@ -44,42 +45,44 @@ class MemorySection(object):
def size(self):
return self.end_addr - self.start_addr
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x0, 0x90000, True , False),
MemorySection(0xd0000, 0xd8000, False, True ),
#MemorySection(0xe0000, 0x1f0000, True , False),
MemorySection(0x200000, 0x228000, False, True ),
MemorySection(0x260000, 0x268000, True , False),
#MemorySection(0x280000, 0x2a0000, True , False),
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x360000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
#MemorySection(0x680000, 0x800000, False, False)
]
SECTIONS = [
MemorySection(0x0, 0x90000, True, False),
MemorySection(0xD0000, 0xD8000, False, True),
# MemorySection(0xe0000, 0x1f0000, True , False),
MemorySection(0x200000, 0x228000, False, True),
MemorySection(0x260000, 0x268000, True, False),
# MemorySection(0x280000, 0x2a0000, True , False),
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x360000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
# MemorySection(0x680000, 0x800000, False, False)
]
# Connection Structure and Table
CONNECTION_ARRAY_ADDRESS = 0x204ba8
CONNECTION_MAX = 11
CONNECTION_ARRAY_ADDRESS = 0x204BA8
CONNECTION_MAX = 11
CONNECTION_STRUCT_LENGTH = 0x150
# Patchram
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = False
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = False
# Heap
BLOC_HEAD = 0x200490 # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
BLOC_HEAD = 0x200490 # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
# Snippet for sendLcpPacket()
SENDLCP_CODE_BASE_ADDRESS = 0x21f000
SENDLCP_CODE_BASE_ADDRESS = 0x21F000
SENDLCP_ASM_CODE = """
push {r4,lr}
+13 -10
View File
@@ -29,15 +29,18 @@ FW_NAME = "BCM4345B0"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x000c07ff, True, False), # Internal ROM
MemorySection(0x000d0000, 0x000dffff, False, True), # Internal Memory Patchram Contents
MemorySection(0x00200400, 0x00201cff, False, True), # Internal Memory Cortex M3
]
SECTIONS = [
MemorySection(0x00000000, 0x000C07FF, True, False), # Internal ROM
MemorySection(
0x000D0000, 0x000DFFFF, False, True
), # Internal Memory Patchram Contents
MemorySection(0x00200400, 0x00201CFF, False, True), # Internal Memory Cortex M3
]
# Patchram
#PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000 #TODO needs to be aligned read
#PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
#PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
#PATCHRAM_NUMBER_OF_SLOTS = 128
#PATCHRAM_ALIGNED = True
# only seems to work 4-byte aligned here ...
# PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000 #TODO needs to be aligned read
# PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
# PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
# PATCHRAM_NUMBER_OF_SLOTS = 128
# PATCHRAM_ALIGNED = True
# only seems to work 4-byte aligned here ...
+11 -8
View File
@@ -29,15 +29,18 @@ FW_NAME = "BCM4345C1"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x000c07ff, True, False), # Internal ROM
MemorySection(0x000d0000, 0x000dffff, False, True), # Internal Memory Patchram Contents
MemorySection(0x00200400, 0x00201cff, False, True), # Internal Memory Cortex M3
]
SECTIONS = [
MemorySection(0x00000000, 0x000C07FF, True, False), # Internal ROM
MemorySection(
0x000D0000, 0x000DFFFF, False, True
), # Internal Memory Patchram Contents
MemorySection(0x00200400, 0x00201CFF, False, True), # Internal Memory Cortex M3
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = False
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = False
# only seems to work 4-byte aligned here ...
+11 -9
View File
@@ -21,15 +21,17 @@
# liability, whether in an action of contract, tort or otherwise, arising from,
# out of or in connection with the Software or the use or other dealings in the
# Software.
from .fw import MemorySection, FirmwareDefinition
from .fw import MemorySection
# Firmware Infos
FW_NAME = "default (unknown firmware)"
class DefaultFirmware(FirmwareDefinition):
# Firmware Infos
FW_NAME = "default (unknown firmware)"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x0, 0x90000, True , False),
MemorySection(0xd0000, 0xd8000, False, True ),
MemorySection(0x200000, 0x228000, False, True )
]
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x0, 0x90000, True, False),
MemorySection(0xD0000, 0xD8000, False, True),
MemorySection(0x200000, 0x228000, False, True),
]
+855 -805
View File
File diff suppressed because it is too large Load Diff
+161 -78
View File
@@ -1,48 +1,66 @@
#!/usr/bin/env python2
from __future__ import absolute_import
import socket
import struct
from future import standard_library
standard_library.install_aliases()
from builtins import str
from builtins import zip
from builtins import range
import subprocess
import datetime
from pwn import *
from internalblue.utils.pwnlib_wrapper import log, context, p32, u16, p16, u32
import fcntl
from .core import InternalBlue
from . import hci
import queue as queue2k
import threading
try:
from typing import List
from typing import List, cast, TYPE_CHECKING
if TYPE_CHECKING:
from internalblue import Device
except:
pass
# from /usr/include/bluetooth/hci.h:
#define HCIDEVUP _IOW('H', 201, int)
#define HCIGETDEVLIST _IOR('H', 210, int)
#define HCIGETDEVINFO _IOR('H', 211, int)
# define HCIDEVUP _IOW('H', 201, int)
# define HCIGETDEVLIST _IOR('H', 210, int)
# define HCIGETDEVINFO _IOR('H', 211, int)
# ioctl numbers. see http://code.activestate.com/recipes/578225-linux-ioctl-numbers-in-python/
def _IOR(type, nr, size):
return 2 << 30 | type << 8 | nr << 0 | size << 16
def _IOW(type, nr, size):
return 1 << 30 | type << 8 | nr << 0 | size << 16
def _IOR(_type, nr, size):
return 2 << 30 | _type << 8 | nr << 0 | size << 16
HCIDEVUP = _IOW(ord('H'), 201, 4)
HCIGETDEVLIST = _IOR(ord('H'), 210, 4)
HCIGETDEVINFO = _IOR(ord('H'), 211, 4)
def _IOW(_type, nr, size):
return 1 << 30 | _type << 8 | nr << 0 | size << 16
HCIDEVUP = _IOW(ord("H"), 201, 4)
HCIGETDEVLIST = _IOR(ord("H"), 210, 4)
HCIGETDEVINFO = _IOR(ord("H"), 211, 4)
class HCICore(InternalBlue):
def __init__(self, queue_size=1000, btsnooplog_filename='btsnoop.log', log_level='info', fix_binutils='True', data_directory=".", replay=False):
super(HCICore, self).__init__(queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory, replay)
def __init__(
self,
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
fix_binutils="True",
data_directory=".",
replay=False,
):
super(HCICore, self).__init__(
queue_size,
btsnooplog_filename,
log_level,
fix_binutils,
data_directory,
replay,
)
self.btsnooplog_file_lock = threading.Lock()
self.serial = False
self.doublecheck = False
@@ -65,43 +83,66 @@ class HCICore(InternalBlue):
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI)
# Ticket 6: does not run on Windows with Kali subsystem
except socket.error:
log.warn("Opening a local Bluetooth socket failed. Not running on native Linux?")
log.warn(
"Opening a local Bluetooth socket failed. Not running on native Linux?"
)
return []
# Do ioctl(s,HCIGETDEVLIST,arg) to get the number of available devices:
# arg is struct hci_dev_list_req (/usr/include/bluetooth/hci.h)
arg = p32(16) # dl->dev_num = HCI_MAX_DEV which is 16 (little endian)
arg += b"\x00"*(8*16)
arg = p32(16) # dl->dev_num = HCI_MAX_DEV which is 16 (little endian)
arg += b"\x00" * (8 * 16)
devices_raw = fcntl.ioctl(s.fileno(), HCIGETDEVLIST, arg)
num_devices = u16(devices_raw[:2])
log.debug("Found %d HCI devices via ioctl(HCIGETDEVLIST)!" % num_devices)
device_list = []
for dev_nr in range(num_devices):
dev_struct_start = 4 + 8*dev_nr
dev_id = u16(devices_raw[dev_struct_start:dev_struct_start+2])
dev_struct_start = 4 + 8 * dev_nr
dev_id = u16(devices_raw[dev_struct_start : dev_struct_start + 2])
# arg is struct hci_dev_info (/usr/include/bluetooth/hci.h)
arg = p16(dev_id) # di->dev_id = <device_id>
arg += b"\x00"*20 # Enough space for name, bdaddr and flags
arg = p16(dev_id) # di->dev_id = <device_id>
arg += b"\x00" * 20 # Enough space for name, bdaddr and flags
dev_info_raw = bytearray(fcntl.ioctl(s.fileno(), HCIGETDEVINFO, arg))
dev_name = dev_info_raw[2:10].replace(b"\x00",b"").decode()
dev_name = dev_info_raw[2:10].replace(b"\x00", b"").decode()
dev_bdaddr = ":".join(["%02X" % x for x in dev_info_raw[10:16][::-1]])
dev_flags = u32(dev_info_raw[16:20])
dev_flags = u32(dev_info_raw[16:20])
if dev_flags == 0:
dev_flags_str = "DOWN"
else:
dev_flags_str = " ".join([name for flag,name in zip(
bin(dev_flags)[2:][::-1],
["UP", "INIT", "RUNNING", "PSCAN", "ISCAN", "AUTH",
"ENCRYPT" , "INQUIRY" , "RAW" , "RESET"]) if flag=="1"])
dev_flags_str = " ".join(
[
name
for flag, name in zip(
bin(dev_flags)[2:][::-1],
[
"UP",
"INIT",
"RUNNING",
"PSCAN",
"ISCAN",
"AUTH",
"ENCRYPT",
"INQUIRY",
"RAW",
"RESET",
],
)
if flag == "1"
]
)
device_list.append({"dev_id": dev_id,
"dev_name": dev_name,
"dev_bdaddr": dev_bdaddr,
"dev_flags": dev_flags,
"dev_flags_str": dev_flags_str})
device_list.append(
{
"dev_id": dev_id,
"dev_name": dev_name,
"dev_bdaddr": dev_bdaddr,
"dev_flags": dev_flags,
"dev_flags_str": dev_flags_str,
}
)
s.close()
return device_list
return cast("List[Device]", device_list)
def bringHciDeviceUp(self, dev_id):
"""
@@ -127,25 +168,36 @@ class HCICore(InternalBlue):
log.warn("Error returned by ioctl: %s" % str(e))
return False
def device_list(self):
"""
Return a list of connected hci devices.
"""
if self.replay:
return [(self, "hci_replay", 'hci: ReplaySocket')]
return [(self, "hci_replay", "hci: ReplaySocket")]
device_list = []
for dev in self.getHciDeviceList():
log.info("HCI device: %s [%s] flags=%d<%s>" %
(dev["dev_name"], dev["dev_bdaddr"],
dev["dev_flags"], dev["dev_flags_str"]))
device_list.append((self, dev["dev_name"], 'hci: %s (%s) <%s>' %
(dev["dev_bdaddr"], dev["dev_name"], dev["dev_flags_str"])))
log.info(
"HCI device: %s [%s] flags=%d<%s>"
% (
dev["dev_name"],
dev["dev_bdaddr"],
dev["dev_flags"],
dev["dev_flags_str"],
)
)
device_list.append(
(
self,
dev["dev_name"],
"hci: %s (%s) <%s>"
% (dev["dev_bdaddr"], dev["dev_name"], dev["dev_flags_str"]),
)
)
if len(device_list) == 0:
log.info('No connected HCI device found')
log.info("No connected HCI device found")
return device_list
return cast("List[Device]", device_list)
def local_connect(self):
"""
@@ -154,7 +206,7 @@ class HCICore(InternalBlue):
if not self.interface:
log.warn("No HCI identifier is set")
return False
if not self._setupSockets():
log.critical("HCI socket could not be established!")
return False
@@ -175,8 +227,10 @@ class HCICore(InternalBlue):
this field as 0x00E03AB44A676000.
"""
time_betw_0_and_2000_ad = int("0x00E03AB44A676000", 16)
time_since_2000_epoch = time - datetime.datetime(2000,1,1)
packed_time = time_since_2000_epoch + datetime.timedelta(microseconds=time_betw_0_and_2000_ad)
time_since_2000_epoch = time - datetime.datetime(2000, 1, 1)
packed_time = time_since_2000_epoch + datetime.timedelta(
microseconds=time_betw_0_and_2000_ad
)
return int(packed_time.total_seconds() * 1000 * 1000)
def _recvThreadFunc(self):
@@ -200,29 +254,47 @@ class HCICore(InternalBlue):
record_data = self.s_snoop.recv(1024)
record_data = bytearray(record_data)
except socket.timeout:
continue # this is ok. just try again without error
continue # this is ok. just try again without error
except Exception as e:
log.critical("Lost device interface with exception {}, terminating receive thread...".format(e))
log.critical(
"Lost device interface with exception {}, terminating receive thread...".format(
e
)
)
self.exit_requested = True
continue
# btsnoop record header data:
btsnoop_orig_len = len(record_data)
btsnoop_inc_len = len(record_data)
btsnoop_flags = 0
btsnoop_drops = 0
btsnoop_time = datetime.datetime.now()
btsnoop_inc_len = len(record_data)
btsnoop_flags = 0
btsnoop_drops = 0
btsnoop_time = datetime.datetime.now()
# Put all relevant infos into a tuple. The HCI packet is parsed with the help of hci.py.
record = (hci.parse_hci_packet(record_data), btsnoop_orig_len, btsnoop_inc_len,
btsnoop_flags, btsnoop_drops, btsnoop_time)
record = (
hci.parse_hci_packet(record_data),
btsnoop_orig_len,
btsnoop_inc_len,
btsnoop_flags,
btsnoop_drops,
btsnoop_time,
)
log.debug("_recvThreadFunc Recv: [" + str(btsnoop_time) + "] " + str(record[0]))
log.debug(
"_recvThreadFunc Recv: [" + str(btsnoop_time) + "] " + str(record[0])
)
# Write to btsnoop file:
if self.write_btsnooplog:
btsnoop_record_hdr = struct.pack(">IIIIq", btsnoop_orig_len, btsnoop_inc_len, btsnoop_flags,
btsnoop_drops, self._btsnoop_pack_time(btsnoop_time))
btsnoop_record_hdr = struct.pack(
">IIIIq",
btsnoop_orig_len,
btsnoop_inc_len,
btsnoop_flags,
btsnoop_drops,
self._btsnoop_pack_time(btsnoop_time),
)
with self.btsnooplog_file_lock:
self.btsnooplog_file.write(btsnoop_record_hdr)
self.btsnooplog_file.write(record_data)
@@ -231,11 +303,13 @@ class HCICore(InternalBlue):
# Put the record into all queues of registeredHciRecvQueues if their
# filter function matches.
for queue, filter_function in self.registeredHciRecvQueues:
if filter_function == None or filter_function(record):
if filter_function is None or filter_function(record):
try:
queue.put(record, block=False)
except queue.Full:
log.warn("recvThreadFunc: A recv queue is full. dropping packets..")
except queue2k.Full:
log.warn(
"recvThreadFunc: A recv queue is full. dropping packets.."
)
# Call all callback functions inside registeredHciCallbacks and pass the
# record as argument.
@@ -244,9 +318,9 @@ class HCICore(InternalBlue):
# Check if the stackDumpReceiver has noticed that the chip crashed.
# if self.stackDumpReceiver.stack_dump_has_happend:
# A stack dump has happend!
# log.warn("recvThreadFunc: The controller send a stack dump.")
# self.exit_requested = True
# A stack dump has happend!
# log.warn("recvThreadFunc: The controller send a stack dump.")
# self.exit_requested = True
log.debug("Receive Thread terminated.")
@@ -257,7 +331,9 @@ class HCICore(InternalBlue):
"""
# Check if hci device is in state "UP". If not, set it to "UP" (requires root)
device = [dev for dev in self.getHciDeviceList() if dev["dev_name"] == self.interface]
device = [
dev for dev in self.getHciDeviceList() if dev["dev_name"] == self.interface
]
if len(device) == 0:
log.warn("Device not found: " + self.interface)
return False
@@ -273,9 +349,11 @@ class HCICore(InternalBlue):
# TODO unload btusb module and check error messages here to give the user some output if sth fails
# Connect to HCI socket
self.s_snoop = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI)
self.s_snoop.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR,1)
self.s_snoop.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP,1)
self.s_snoop = socket.socket(
socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI
)
self.s_snoop.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR, 1)
self.s_snoop.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP, 1)
"""
struct hci_filter {
uint32_t type_mask; -> 4
@@ -284,11 +362,14 @@ class HCICore(InternalBlue):
};
"""
# TODO still seems to only forward incoming events?!
self.s_snoop.setsockopt(socket.SOL_HCI, socket.HCI_FILTER,
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00') #type mask, event mask, event mask, opcode
self.s_snoop.setsockopt(
socket.SOL_HCI,
socket.HCI_FILTER,
b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00",
) # type mask, event mask, event mask, opcode
interface_num = device["dev_id"]
log.debug("Socket interface number: %s" % (interface_num))
log.debug("Socket interface number: %s" % interface_num)
self.s_snoop.bind((interface_num,))
self.s_snoop.settimeout(2)
log.debug("_setupSockets: Bound socket.")
@@ -299,7 +380,9 @@ class HCICore(InternalBlue):
# Write Header to btsnoop file (if file is still empty):
if self.write_btsnooplog and self.btsnooplog_file.tell() == 0:
# BT Snoop Header: btsnoop\x00, version: 1, data link type: 1002
btsnoop_hdr = b"btsnoop\x00" + p32(1,endian="big") + p32(1002,endian="big")
btsnoop_hdr = (
b"btsnoop\x00" + p32(1, endian="big") + p32(1002, endian="big")
)
with self.btsnooplog_file_lock:
self.btsnooplog_file.write(btsnoop_hdr)
self.btsnooplog_file.flush()
@@ -311,9 +394,9 @@ class HCICore(InternalBlue):
Close s_snoop and s_inject socket. (equal)
"""
if (self.s_inject != None):
if self.s_inject is not None:
self.s_inject.close()
self.s_inject = None
self.s_snoop = None
self.s_snoop = None
return True
+67 -35
View File
@@ -1,22 +1,38 @@
#!/usr/bin/env python2
from __future__ import absolute_import
import struct
from future import standard_library
standard_library.install_aliases()
from builtins import str
import socket
import queue as queue2k
from . import hci
from pwn import *
from internalblue.utils.pwnlib_wrapper import log, context
from .core import InternalBlue
class iOSCore(InternalBlue):
def __init__(self, ios_addr, queue_size=1000, btsnooplog_filename='btsnoop.log', log_level='info', fix_binutils='True', data_directory="."):
super(iOSCore, self).__init__(queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory=".")
parts = ios_addr.split(':')
class iOSCore(InternalBlue):
buffer: bytes
def __init__(
self,
ios_addr,
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
fix_binutils="True",
data_directory=".",
):
super(iOSCore, self).__init__(
queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory="."
)
parts = ios_addr.split(":")
if len(parts) != 2:
log.critical("iOS device address should be of format HOSTNAME:PORT")
exit(-1)
@@ -24,7 +40,7 @@ class iOSCore(InternalBlue):
self.ios_port = parts[1]
self.serial = False
self.doublecheck = True
self.buffer = ""
self.buffer = b""
def device_list(self):
"""
@@ -62,7 +78,7 @@ class iOSCore(InternalBlue):
except queue2k.Empty:
log.warn("sendH4: waiting for response timed out!")
return None
except queue.Full:
except queue2k.Full:
log.warn("sendH4: send queue is full!")
return None
@@ -73,7 +89,9 @@ class iOSCore(InternalBlue):
"""
if not self._setupSockets():
log.critical("No connection to iPhone.")
log.info("Check if\n -> Bluetooth is deactivated in the iPhone settings\n -> internalblue-ios-proxy is running\n -> the proxied port is accesible from this machine")
log.info(
"Check if\n -> Bluetooth is deactivated in the iPhone settings\n -> internalblue-ios-proxy is running\n -> the proxied port is accesible from this machine"
)
return False
return True
@@ -95,50 +113,56 @@ class iOSCore(InternalBlue):
return True
def _getLatestH4Blob(self, new_data=""):
data_out = ""
def _getLatestH4Blob(self, new_data: bytes = b""):
data_out: bytes = b""
self.buffer += new_data
if len(self.buffer) > 0:
# if the buffer is too small, wait for more data
if len(self.buffer) < 5:
return (None, False)
else:
#log.info(self.buffer[0].encode("hex"))
# log.info(self.buffer[0].encode("hex"))
# for ACL data the length field is at offset 3
if self.buffer[0] == '\x02':
if self.buffer[0] == 0x2:
acl_len = struct.unpack_from("h", self.buffer[3:])[0]
required_len = acl_len + 5
# for HCI cmd data the length is at offset 3 (but just one byte)
elif self.buffer[0] == '\x01':
elif self.buffer[0] == 0x1:
hci_len = struct.unpack_from("b", self.buffer[3:])[0]
required_len = hci_len + 4
# for HCI event data the length is at offset 2 (one byte)
elif self.buffer[0] == '\x04':
elif self.buffer[0] == 0x4:
hci_len = struct.unpack_from("b", self.buffer[2:])[0]
required_len = hci_len + 3
# for BCM data the length should always be 64
elif self.buffer[0] == '\x07':
elif self.buffer[0] == 0x07:
required_len = 64
else:
raise ValueError("Could not derive required_len from buffer")
# if we don't have all the data we need, we just wait for more
if len(self.buffer) < required_len:
#log.info("Not enough data, expected %d, got %d", required_len, len(self.buffer))
# log.info("Not enough data, expected %d, got %d", required_len, len(self.buffer))
return (None, False)
# might be the case that we have too much
elif len(self.buffer) > required_len:
log.info("Got too much data, expected %d, got %d", required_len, len(self.buffer))
surplus = len(self.buffer) - required_len
new_buffer = self.buffer[required_len:len(self.buffer)]
log.info(
"Got too much data, expected %d, got %d",
required_len,
len(self.buffer),
)
surplus = len(self.buffer) - required_len
new_buffer = self.buffer[required_len : len(self.buffer)]
data_out = self.buffer[:-surplus]
#log.info("new_buffer: %s, data_out: %s", new_buffer.encode("hex"), data_out.encode("hex"))
# log.info("new_buffer: %s, data_out: %s", new_buffer.encode("hex"), data_out.encode("hex"))
self.buffer = new_buffer
return (data_out, True)
# sometimes we even have just the right amout of data
else:
#log.info("Got exactly the right amount of data")
# log.info("Got exactly the right amount of data")
data_out = self.buffer
self.buffer = ""
self.buffer = b""
return (data_out, False)
else:
return (None, False)
@@ -158,27 +182,34 @@ class iOSCore(InternalBlue):
try:
received_data = self.s_snoop.recv(1024)
except socket.timeout:
continue # this is ok. just try again without error
continue # this is ok. just try again without error
# because the iOS socket is rather unreliable (blame the iOS proxy developer) we
# need to do some length checks and get the H4/HCI data in the right format
#log.info("H4 Data received")
#log.info(received_data.encode('hex'))
# log.info("H4 Data received")
# log.info(received_data.encode('hex'))
(record_data, is_more) = self._getLatestH4Blob(new_data=received_data)
while record_data is not None:
# Put all relevant infos into a tuple. The HCI packet is parsed with the help of hci.py.
record = (hci.parse_hci_packet(record_data), 0, 0, 0, 0, 0)
record = (hci.parse_hci_packet(record_data), 0, 0, 0, 0, 0)
log.debug("Recv: " + str(record[0]))
# Put the record into all queues of registeredHciRecvQueues if their
# filter function matches.
for queue, filter_function in self.registeredHciRecvQueues: # TODO filter_function not working with bluez modifications
for (
queue,
filter_function,
) in (
self.registeredHciRecvQueues
): # TODO filter_function not working with bluez modifications
try:
queue.put(record, block=False)
except queue.Full:
log.warn("recvThreadFunc: A recv queue is full. dropping packets..")
log.warn(
"recvThreadFunc: A recv queue is full. dropping packets.."
)
# Call all callback functions inside registeredHciCallbacks and pass the
# record as argument.
@@ -188,9 +219,11 @@ class iOSCore(InternalBlue):
# Check if the stackDumpReceiver has noticed that the chip crashed.
if self.stackDumpReceiver.stack_dump_has_happend:
# A stack dump has happend!
log.warn("recvThreadFunc: The controller send a stack dump. stopping..")
log.warn(
"recvThreadFunc: The controller send a stack dump. stopping.."
)
self.exit_requested = True
(record_data, is_more) = self._getLatestH4Blob()
if not is_more:
break
@@ -202,9 +235,8 @@ class iOSCore(InternalBlue):
Close s_snoop and s_inject (which are the same)
"""
if (self.s_inject != None):
if self.s_inject is not None:
self.s_inject.close()
self.s_inject = None
return True
+80 -25
View File
@@ -1,35 +1,62 @@
#!/usr/bin/env python2
from __future__ import absolute_import
import random
import time
from future import standard_library
standard_library.install_aliases()
from builtins import str
import socket
import queue as queue2k
from . import hci
from pwn import *
from internalblue.utils.pwnlib_wrapper import log, context, p8
from .core import InternalBlue
import binascii
import os
filepath = os.path.dirname(os.path.abspath(__file__))
class macOSCore(InternalBlue):
IOBE = None
def __init__(self, queue_size=1000, btsnooplog_filename='btsnoop.log', log_level='info', fix_binutils='True', data_directory=".", replay=False):
super(macOSCore, self).__init__(queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory=".", replay=replay)
class macOSCore(InternalBlue):
def __init__(
self,
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
fix_binutils="True",
data_directory=".",
replay=False,
):
super(macOSCore, self).__init__(
queue_size,
btsnooplog_filename,
log_level,
fix_binutils,
data_directory=".",
replay=replay,
)
self.doublecheck = False
self.iobe = None
self.serial = None
if not replay:
import objc
objc.initFrameworkWrapper("IOBluetoothExtended",
frameworkIdentifier="de.tu-darmstadt.seemoo.IOBluetoothExtended",
frameworkPath=objc.pathForFramework(
filepath + "/../macos-framework/IOBluetoothExtended.framework"),
globals=globals())
import objc # type: ignore
objc.initFrameworkWrapper(
"IOBluetoothExtended",
frameworkIdentifier="de.tu-darmstadt.seemoo.IOBluetoothExtended",
frameworkPath=objc.pathForFramework(
filepath + "/../macos-framework/IOBluetoothExtended.framework"
),
globals=globals(),
)
self.hciport = -1
def device_list(self):
@@ -56,14 +83,20 @@ class macOSCore(InternalBlue):
return True
def _setupSockets(self):
self.hciport = random.randint(60000, 65535-1)
log.debug("_setupSockets: Selected random ports snoop=%d and inject=%d" % (self.hciport, self.hciport + 1))
log.info("Wireshark configuration (on Loopback interface): udp.port == %d || udp.port == %d" % (self.hciport, self.hciport + 1))
self.hciport = random.randint(60000, 65535 - 1)
log.debug(
"_setupSockets: Selected random ports snoop=%d and inject=%d"
% (self.hciport, self.hciport + 1)
)
log.info(
"Wireshark configuration (on Loopback interface): udp.port == %d || udp.port == %d"
% (self.hciport, self.hciport + 1)
)
# Create s_snoop socket
self.s_snoop = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.s_snoop.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.s_snoop.bind(('127.0.0.1', self.hciport))
self.s_snoop.bind(("127.0.0.1", self.hciport))
self.s_snoop.settimeout(0.5)
self.s_snoop.setblocking(True)
@@ -75,7 +108,9 @@ class macOSCore(InternalBlue):
# Create IOBluetoothExtended Object that listens for commands,
# sends them to the Bluetooth chip and replies via UDP socket.
if not self.replay:
self.iobe = IOBE.alloc().initWith_and_(str(self.hciport+1), str(self.hciport))
self.iobe = IOBE.alloc().initWith_and_(
str(self.hciport + 1), str(self.hciport)
)
else:
self.iobe = None
time.sleep(0.5)
@@ -95,20 +130,35 @@ class macOSCore(InternalBlue):
data, addr = self.s_snoop.recvfrom(1024)
record_data = bytearray(data)
except socket.timeout:
continue # this is ok. just try again without error
continue # this is ok. just try again without error
if not self.exit_requested:
# Put all relevant infos into a tuple. The HCI packet is parsed with the help of hci.py.
record = (hci.parse_hci_packet(record_data), 0, 0, 0, 0, 0) #TODO not sure if this causes trouble?
record = (
hci.parse_hci_packet(record_data),
0,
0,
0,
0,
0,
) # TODO not sure if this causes trouble?
log.debug("Recv: " + str(record[0]))
# Put the record into all queues of registeredHciRecvQueues if their
# filter function matches.
for queue, filter_function in self.registeredHciRecvQueues: # TODO filter_function not working with bluez modifications
for (
queue,
filter_function,
) in (
self.registeredHciRecvQueues
): # TODO filter_function not working with bluez modifications
try:
queue.put(record, block=False)
except queue.Full:
log.warn("recvThreadFunc: A recv queue is full. dropping packets..>" + record_data)
log.warn(
"recvThreadFunc: A recv queue is full. dropping packets..>"
+ str(record_data)
)
# Call all callback functions inside registeredHciCallbacks and pass the
# record as argument.
@@ -138,11 +188,16 @@ class macOSCore(InternalBlue):
# Send command to the chip using IOBluetoothExtended framework
h4type, data, queue, filter_function = task
data = bytearray(data)
opcode = format(data[1], '02x') + format(data[0], '02x')
opcode = format(data[1], "02x") + format(data[0], "02x")
log.debug("Sending command: 0x" + ''.join(format(x, '02x') for x in data) + ", opcode: " + opcode)
log.debug(
"Sending command: 0x"
+ "".join(format(x, "02x") for x in data)
+ ", opcode: "
+ opcode
)
if not(h4type == 0x01 or h4type == 0x02):
if not (h4type == 0x01 or h4type == 0x02):
log.warn("H4 Type {0} not supported by macOS Core!".format(str(h4type)))
if queue is not None:
queue.put(None)
@@ -154,7 +209,7 @@ class macOSCore(InternalBlue):
self.registerHciRecvQueue(recvQueue, filter_function)
# Sending command
self.s_inject.sendto(out, ('127.0.0.1', self.hciport+1))
self.s_inject.sendto(out, ("127.0.0.1", self.hciport + 1))
# if the caller expects a response:
# Wait for the HCI event response by polling the recvQueue
@@ -162,7 +217,7 @@ class macOSCore(InternalBlue):
try:
record = recvQueue.get(timeout=10)
hcipkt = record[0]
data = hcipkt.data
data = hcipkt.data
except queue2k.Empty:
log.warn("_sendThreadFunc: No response from the firmware.")
data = None
@@ -188,5 +243,5 @@ class macOSCore(InternalBlue):
def shutdown(self):
if not self.replay:
self.iobe.shutdown()
self.s_inject.sendto(b'', ('127.0.0.1', self.s_snoop.getsockname()[1]))
self.s_inject.sendto(b"", ("127.0.0.1", self.s_snoop.getsockname()[1]))
super(macOSCore, self).shutdown()
+32 -14
View File
@@ -1,5 +1,6 @@
from builtins import object
from pwnlib.util.packing import u32, u16, u8
from internalblue.utils.pwnlib_wrapper import u32, u16, u8
from typing import Any
class ConnectionInformation(object):
@@ -8,7 +9,7 @@ class ConnectionInformation(object):
master_of_connection = False
remote_name_address = 0
remote_address = None
id = None
id: bytearray
public_rand = None
extended_lmp_feat = None
link_key = None
@@ -16,8 +17,21 @@ class ConnectionInformation(object):
effective_key_len = 0
host_supported_feat = None
def __init__(self, connection_number, remote_address, remote_name_address, master_of_connection, connection_handle,
public_rand, effective_key_len, link_key, tx_pwr_lvl_dBm, extended_lmp_feat, host_supported_feat, id):
def __init__(
self,
connection_number,
remote_address,
remote_name_address,
master_of_connection,
connection_handle,
public_rand,
effective_key_len,
link_key,
tx_pwr_lvl_dBm,
extended_lmp_feat,
host_supported_feat,
id,
):
self.connection_number = connection_number
self.remote_address = remote_address
self.remote_name_address = remote_name_address
@@ -35,16 +49,20 @@ class ConnectionInformation(object):
def from_connection_buffer(connection):
# Possible TODO: Convert this to a Katai Struct parser with a proper .ksy grammar.
return ConnectionInformation(u32(connection[:4]), connection[0x28:0x2E][::-1],
u32(connection[0x4C:0x50]),
u32(connection[0x1C:0x20]) & 1 << 15 == 0,
u16(connection[0x64:0x66]),
connection[0x78:0x88],
u8(connection[0xa7:0xa8]),
connection[0x68:0x68 + u8(connection[0xa7:0xa8])],
u8(connection[0x9c:0x9d]) - 127,
connection[0x30:0x38], connection[0x38:0x40],
connection[0x0c:0x0d])
return ConnectionInformation(
u32(connection[:4]),
connection[0x28:0x2E][::-1],
u32(connection[0x4C:0x50]),
u32(connection[0x1C:0x20]) & 1 << 15 == 0,
u16(connection[0x64:0x66]),
connection[0x78:0x88],
u8(connection[0xA7:0xA8]),
connection[0x68 : 0x68 + u8(connection[0xA7:0xA8])],
u8(connection[0x9C:0x9D]) - 127,
connection[0x30:0x38],
connection[0x38:0x40],
connection[0x0C:0x0D],
)
# For some reason the following doesn't work because some attributes like link_key end up as one element tuples
# connection_number = u32(connection[:4])
# remote_address = connection[0x28:0x2E][::-1],
+23 -4
View File
@@ -1,10 +1,13 @@
from builtins import object
from typing import Any
class QueueElement(object):
index = 0
next_item = 0
prev = 0
capacity = 0
name = ''
name = ""
queue_buf_start = 0
available_items = 0
item_size = 0
@@ -16,8 +19,24 @@ class QueueElement(object):
queue_buf_end = 0
thread_waitlist = 0
def __init__(self, index, address, item_size, capacity, available_items, free_slots, queue_buf_start, queue_buf_end,
next_item, next_free_slot, thread_waitlist, waitlist_length, next, prev, name):
def __init__(
self,
index,
address,
item_size,
capacity,
available_items,
free_slots,
queue_buf_start,
queue_buf_end,
next_item,
next_free_slot,
thread_waitlist,
waitlist_length,
next,
prev,
name,
):
self.index = index
self.next_item = next_item
self.prev = prev
@@ -36,4 +55,4 @@ class QueueElement(object):
def __getitem__(self, item):
# type: (str) -> Any
return vars(self)[item]
return vars(self)[item]
+17 -16
View File
@@ -45,12 +45,13 @@ class SocketRecvHook(object):
self.recvfrom_hook(data, addr)
return data, addr
class SocketInjectHook(object):
def __init__(self, socket, core):
# type: (socket.socket, InternalBlue) -> None
self.inject_socket = socket
self.replace = False
self.core = core # type: InternalBlue
self.core = core # type: InternalBlue
def close(self):
if self.inject_socket:
@@ -105,7 +106,6 @@ class SocketInjectHook(object):
class SocketDuplexHook(SocketInjectHook, SocketRecvHook):
def __init__(self, snoop_socket, inject_socket, core, **kwargs):
# type: (socket.socket, socket.socket, InternalBlue, Dict[str, Any]) -> None
self.snoop_socket = snoop_socket
@@ -125,10 +125,10 @@ class HookBase(object):
class TraceToFileHook(SocketDuplexHook):
def __init__(self, snoop_socket, inject_socket, core, filename='/tmp/bt_hci.log'):
def __init__(self, snoop_socket, inject_socket, core, filename="/tmp/bt_hci.log"):
# type: (socket.socket, socket.socket, InternalBlue, str) -> None
SocketDuplexHook.__init__(self, snoop_socket, inject_socket, core)
self.file = open(filename, 'a')
self.file = open(filename, "a")
self.replace = False
self.log = []
self.closed = False
@@ -167,11 +167,11 @@ class TraceToFileHook(SocketDuplexHook):
self.file.close()
self.closed = True
import socket
class PrintTrace(SocketDuplexHook):
def send_hook(self, data, **kwargs):
print("Sent: {}".format(binascii.hexlify(data)))
@@ -189,10 +189,13 @@ class PrintTrace(SocketDuplexHook):
class ReplaySocket(SocketDuplexHook):
def __init__(self, snoop_socket, inject_socket, core, filename='/tmp/bt_hci.log', debug=False):
def __init__(
self, snoop_socket, inject_socket, core, filename="/tmp/bt_hci.log", debug=False
):
SocketDuplexHook.__init__(self, snoop_socket, inject_socket, core)
self.replace = True
self.log = open(filename).readlines()
with open(filename) as f:
self.log = f.readlines()
self.index = 0
self.debug = debug
if self.log[0].startswith("#"):
@@ -226,8 +229,8 @@ class ReplaySocket(SocketDuplexHook):
# Some recieves aren't handled yet, wait a bit so the recv thread takes care of them.
time.sleep(0.2)
direction, encoded_data = self.log[self.index].split(" ", 1)
assert (direction == "TX")
log_data = binascii.unhexlify(encoded_data.rstrip('\n'))
assert direction == "TX"
log_data = binascii.unhexlify(encoded_data.rstrip("\n"))
assert data == log_data, "Got {}, expected {}".format(hex_data, encoded_data)
self.index += 1
ty, data = self.log[self.index].split(" ", 1)
@@ -240,7 +243,7 @@ class ReplaySocket(SocketDuplexHook):
direction, encoded_data = self.log[self.index].split(" ", 1)
if direction == "RX":
self.index += 1
return binascii.unhexlify(encoded_data.rstrip('\n'))
return binascii.unhexlify(encoded_data.rstrip("\n"))
else:
raise socket.timeout()
@@ -249,7 +252,7 @@ class ReplaySocket(SocketDuplexHook):
direction, encoded_data = self.log[self.index].split(" ", 1)
if direction == "RX":
self.index += 1
return binascii.unhexlify(encoded_data.rstrip('\n')), 1234
return binascii.unhexlify(encoded_data.rstrip("\n")), 1234
else:
raise socket.timeout()
@@ -259,11 +262,10 @@ class ReplaySocket(SocketDuplexHook):
def close(self):
assert self.index + 1 == len(self.log)
from internalblue.core import InternalBlue
def hook(core, socket_hook, **hookkwargs):
# type: (Type[InternalBlue], Type[SocketDuplexHook], Any) -> None
@@ -289,19 +291,18 @@ def hook(core, socket_hook, **hookkwargs):
else:
self.s_inject.close()
self.s_snoop.close()
return wrapped_teardown_sockets
core._teardownSockets = wrap_teardown_sockets(core._teardownSockets)
def wrap_device_list(orig_func):
def wrapped_device_list(self, *args, **kwargs):
if not self.replay:
return orig_func(self, *args, **kwargs)
else:
return [(self, "ReplayDevice", "ReplayDevice")]
return wrapped_device_list
core.device_list = wrap_device_list(core.device_list)
-163
View File
@@ -1,163 +0,0 @@
#!/usr/bin/env python2
from __future__ import print_function
from __future__ import absolute_import
from future import standard_library
standard_library.install_aliases()
import socket
import queue as queue2k
from . import hci
from pwn import *
from .core import InternalBlue
import binascii
filepath = os.path.dirname(os.path.abspath(__file__))
try:
import typing
from typing import List, Tuple, Any
from internalblue.core import InternalBlue
except:
pass
class testCore(InternalBlue):
def __init__(self, queue_size=1000, btsnooplog_filename='btsnoop.log', log_level='info', fix_binutils='True', data_directory="."):
super(testCore, self).__init__(queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory=".")
file = open(filepath+'/../dummymemdump.bin', mode='rb')
self.memory = file.read()
file.close()
self.doublecheck = False
def device_list(self):
# type: () -> List[Tuple[InternalBlue,str,str]]
"""
Get a list of connected devices
"""
if self.exit_requested:
self.shutdown()
if self.running:
log.warn("Already running. Call shutdown() first!")
return []
# assume that a explicitly specified iPhone exists
device_list = [(self, "Testchip", "Testchip")]
return device_list
def sendH4(self, h4type, data, timeout=2):
"""
Send an arbitrary HCI packet by pushing a send-task into the
sendQueue. This function blocks until the response is received
or the timeout expires. The return value is the Payload of the
HCI Command Complete Event which was received in response to
the command or None if no response was received within the timeout.
"""
queue = queue2k.Queue(1)
try:
self.sendQueue.put((h4type, data, queue, None), timeout=timeout)
ret = queue.get(timeout=timeout)
return ret
except queue2k.Empty:
log.warn("sendH4: waiting for response timed out!")
return None
except queue.Full:
log.warn("sendH4: send queue is full!")
return None
def local_connect(self):
return True
def _setupSockets(self):
self.hciport = random.randint(60000, 65535 - 1)
log.debug("_setupSockets: Selected random ports snoop=%d and inject=%d" % (self.hciport, self.hciport + 1))
log.info("Wireshark configuration (on Loopback interface): udp.port == %d || udp.port == %d" % (
self.hciport, self.hciport + 1))
# Create s_snoop socket
self.s_snoop = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s_snoop.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.s_snoop.bind(('127.0.0.1', self.hciport))
self.s_snoop.settimeout(0.5)
self.s_snoop.setblocking(True)
# Create s_inject
self.s_inject = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s_inject.settimeout(0.5)
self.s_inject.setblocking(True)
time.sleep(1.5)
return True
def _recvThreadFunc(self):
log.debug("Receive Thread terminated.")
def _sendThreadFunc(self):
log.debug("Send Thread started.")
while not self.exit_requested:
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
context.log_level = self.log_level
# Wait for 'send task' in send queue
try:
task = self.sendQueue.get(timeout=0.5)
except queue2k.Empty:
continue
# Extract the components of the task
h4type, data, queue, filter_function = task
# Prepend UART TYPE and length.
out = p8(h4type) + p8(len(data)) + data
# Send command to the chip using IOBluetoothExtended framework
h4type, data, queue, filter_function = task
opcode = binascii.hexlify(data[1]) + binascii.hexlify(data[0])
log.debug("Sending command: 0x" + binascii.hexlify(data))
# if the caller expects a response: register a queue to receive the response
if queue is not None and filter_function is not None:
recvQueue = queue2k.Queue(1)
self.registerHciRecvQueue(recvQueue, filter_function)
# if the caller expects a response:
# Wait for the HCI event response by polling the recvQueue
if queue is not None and filter_function is not None:
# Return responses according to the opcode & operands
if opcode == '1001':
record_data = '040E0C0101100006b415060f000e22'.decode('hex')
data = hci.parse_hci_packet(record_data).data
elif opcode == 'fc4d':
length = int(binascii.hexlify(data[7]), 16)
address = int(binascii.hexlify(data[6]+data[5]+data[4]+data[3]), 16)
data = '014dfc00'.decode('hex') + self.memory[address:address+length]
elif opcode == 'fc4c':
log.info(data.encode('hex'))
length = int(binascii.hexlify(data[2]), 16)
address = int(binascii.hexlify(data[6]+data[5]+data[4]+data[3]), 16)
self.memory = self.memory[:address] + data[7:len(data)] + self.memory[address+length:]
else:
print(opcode)
queue.put(data)
self.unregisterHciRecvQueue(recvQueue)
log.debug("Send Thread terminated.")
def enableBroadcomDiagnosticLogging(self, enable):
return
def _teardownSockets(self):
return True
def shutdown(self):
return True
-3
View File
@@ -1,3 +0,0 @@
def bytes_to_hex(bytes):
# type: (bytearray) -> str
return ''.join(format(x, '02x') for x in bytearray(bytes))
+7
View File
@@ -0,0 +1,7 @@
# from pwnlib.util.packing import *
from typing import Union
def bytes_to_hex(bytes):
# type: (Union[bytes, bytearray]) -> str
return "".join(format(x, "02x") for x in bytearray(bytes))
+95
View File
@@ -0,0 +1,95 @@
"""
The following proxies various utilities from pwnlibs by explicitly importing them
To replace a "from pwn import *" remove it and let your IDE highlight all missing methods (Hint: F2 in PyCharm goes to next error)
import the missing (and only the missing!) methods from this module, e.g. with "from internalblue.utils import term, read, log, text, options"
In some cases like "from pwn import socket" this just imports another module.
Use an IPython shell to run "from pwn import *" and check where some method/module actually comes from and either import it directly or add it to this module
"""
# Imports that used to be imported via 'from pwn import *'
import pwnlib
from pwnlib import term
from pwnlib.util import iters
from pwnlib.util.misc import read
from pwnlib.context import context
# TODO: Logging via pwnlib doesn't work yet, so for now it is still used via pwn
# import pwnlib.log
# pwnlib.log.install_default_handler()
# log = pwnlib.log.getLogger('internalbue')
from pwn import log
from pwnlib.term import text
from pwnlib.ui import options, yesno
from pwnlib.util.packing import flat
from pwnlib.asm import disasm
from pwnlib.util.fiddling import isprint, unbits, bits_str, bits
"""
The packers like u8 are generated in a fairly convoluted way that breaks IDE introspection.
The following code remedies this by:
- Explicitly defining a stub function with type annotations
- Generating all the packers like pwnlibs would
- Only if if the current module already has the name of the packer as an attribute (i.e. has a stub function defined) it will be replaced with the pwnlibs version
This means:
- All import issues in the rest of the code are genuine as the imports are only available if an explicit stub function is added
- The functions can be easily replaced by just implementing them and removing the for loop at the end
"""
# Imports needed for this hack
from pwnlib.util.packing import ops, sizes, make_multi
import sys
try:
from typing import Union, Optional, Literal
endianess = Union[Literal["big"]]
except ImportError:
pass
mod = sys.modules[__name__]
_DEFINES = ["u8", "p8", "u32", "u16", "p32"]
def u8(data, endian=None):
# type: (bytes, Optional[endianess]) -> int
pass
def p8(number, endian=None):
# type: (int, Optional[endianess]) -> bytes
pass
def u16(data, endian=None):
# type: (bytes, Optional[endianess]) -> int
pass
def p16(number, endian=None):
# type: (int, Optional[endianess]) -> bytes
pass
def u32(data, endian=None):
# type: (bytes, Optional[endianess]) -> int
pass
def p32(number, endian=None):
# type: (int, Optional[endianess]) -> bytes
pass
for op, size in iters.product(ops, sizes):
name, routine = make_multi(op, size)
if hasattr(mod, name):
setattr(mod, name, routine)
+25
View File
@@ -0,0 +1,25 @@
[mypy-internalblue]
python_version = 3.8
warn_return_any = True
[mypy-internalblue.fw.*]
ignore_errors = True
[mypy-internalblue.socket_hooks]
ignore_errors = True
[mypy-pwnlib.*]
ignore_missing_imports = True
[mypy-future.*]
ignore_missing_imports = True
[mypy-past.*]
ignore_missing_imports = True
[mypy-IPython]
ignore_missing_imports = True
+22 -19
View File
@@ -2,22 +2,25 @@
from setuptools import setup
setup(name='internalblue',
version='0.3',
description='A Bluetooth Experimentation Framework based on the Broadcom Bluetooth Controller Family.',
url='http://github.com/seemoo-lab/internalblue',
author='Dennis Mantz',
author_email='dennis.mantz@googlemail.com',
license='MIT',
packages=['internalblue', 'internalblue/fw'],
install_requires=[
'pwntools>=4.2.0.dev0',
'pyelftools',
],
extras_require={
"macoscore": ["pyobjc"],
},
entry_points = {
'console_scripts': ['internalblue=internalblue.cli:internalblue_cli']
},
zip_safe=False)
setup(
name="internalblue",
version="0.3",
description="A Bluetooth Experimentation Framework based on the Broadcom Bluetooth Controller Family.",
url="http://github.com/seemoo-lab/internalblue",
author="Dennis Mantz",
author_email="dennis.mantz@googlemail.com",
license="MIT",
packages=[
"internalblue",
"internalblue/fw",
"internalblue/objects",
"internalblue/utils",
],
install_requires=["pwntools>=4.0.1", "pyelftools", "future"],
extras_require={"macoscore": ["pyobjc"], "ipython": ["IPython"]},
tests_require=["nose", "pytest", "pwntools>=4.2.0.dev0"],
entry_points={
"console_scripts": ["internalblue=internalblue.cli:internalblue_entry_point"]
},
zip_safe=False,
)
-17
View File
@@ -1,17 +0,0 @@
import unittest
from internalblue.testcore import testCore
class DummyCoreTest(unittest.TestCase):
def setUp(self):
t = testCore(log_level='debug', data_directory='/tmp')
dev = t.device_list()[0]
reference = dev[0]
reference.interface = dev[1]
self.assert_(reference.connect(), 'Connect failed')
self.reference = reference
def tearDown(self):
self.reference.shutdown()