Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 692134f748 | |||
| c2166ce384 | |||
| a7266c819d | |||
| f6704f904e | |||
| 9ed9f6e1cc | |||
| efe3614ea5 | |||
| e6b58865dc | |||
| 8d14ab9485 | |||
| f4f51a7952 | |||
| b409207a3e | |||
| fa18727e69 | |||
| 6255023db8 | |||
| 796eb4cc03 | |||
| 6677b86e94 | |||
| ca070290c5 | |||
| bc3d52f00e | |||
| d737068304 | |||
| e8f6e94e1b | |||
| 6e91f9c718 | |||
| 361892bc06 | |||
| 2ce2224421 | |||
| 104a35a79a | |||
| e6b99906c9 |
+31
-14
@@ -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,
|
||||
|
||||
+141
-64
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+666
-304
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
from .fw import FirmwareDefinition
|
||||
|
||||
+102
-18
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
|
||||
@@ -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 ...
|
||||
|
||||
@@ -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 ...
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
|
||||
@@ -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()
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
)
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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 ...
|
||||
|
||||
@@ -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 ...
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+161
-78
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -1,3 +0,0 @@
|
||||
def bytes_to_hex(bytes):
|
||||
# type: (bytearray) -> str
|
||||
return ''.join(format(x, '02x') for x in bytearray(bytes))
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user