Created
February 27, 2018 12:34
-
-
Save Miss-Inputs/9fddf201a0187f62bb3aed81bb5ea91e to your computer and use it in GitHub Desktop.
Read info from DS firmware dump
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import sys | |
import calendar | |
CONSOLE_TYPES = { | |
0xff: 'DS', | |
0x20: 'DS Lite', | |
0x57: 'DSi (or 3DS)', | |
0x43: 'iQue DS', | |
0x63: 'iQue DS Lite' | |
} | |
COLOURS = { | |
1: 'Gray', | |
2: 'Brown', | |
3: 'Red', | |
4: 'Light Pink', | |
5: 'Orange', | |
6: 'Yellow', | |
7: 'Light Green', | |
8: 'Green', | |
9: 'Dark Green', | |
10: 'Teal', | |
11: 'Blue', | |
12: 'Dark Blue', | |
13: 'Dark Purple', | |
14: 'Purple', | |
15: 'Pink', | |
} | |
WIFI_STATUSES = { | |
0: 'Normal', | |
1: 'AOSS', | |
16: 'WPA/WPA2', | |
255: 'Unconfigured' | |
} | |
WPA_TYPES = { | |
0: 'None/WEP', | |
4: 'WPA-TKIP', | |
5: 'WPA2-TKIP', | |
6: 'WPA-AES', | |
7: 'WPA2-AES', | |
} | |
LANGUAGES = { | |
0: 'Japanese', | |
1: 'English', | |
2: 'French', | |
3: 'German', | |
4: 'Italian', | |
5: 'Spanish', | |
6: 'Chinese', | |
7: 'Reserved', | |
} | |
def format_ip_address(b): | |
#Apparently I can't just use str.join for this thanks I hate it | |
return '{0}.{1}.{2}.{3}'.format(*b) | |
def is_null_ip_address(b): | |
return b[0] == 0 and b[1] == 0 and b[2] == 0 and b[3] == 0 | |
def read_wifi_config(f, offset, slot_number): | |
f.seek(offset + 0xe7) | |
status = f.read(1)[0] | |
if status == 0xff: | |
return | |
print('Wifi %d status: %s' % (slot_number, WIFI_STATUSES.get(status, 'Unknown 0x{0:02X}'.format(status)))) | |
f.seek(offset + 0x40) | |
ssid = f.read(32).decode('ascii', errors='backslashreplace').rstrip('\0') | |
print('Wifi %d SSID: %s' % (slot_number, ssid)) | |
ssid_aoss = f.read(32).decode('ascii', errors='backslashreplace').rstrip('\0') | |
print('Wifi %d SSID for AOSS: %s' % (slot_number, ssid_aoss)) | |
f.seek(offset + 0xe6) | |
wep_mode = f.read(1)[0] | |
#0: None, 1 = 5 hex, 2 = 13 hex, 3 = 16 hex, 5 = 5 ascii, 6 = 13 byte, 7 = 16 byte | |
print('Wifi %d WEP mode: %d' % (slot_number, wep_mode)) | |
f.seek(offset + 0xea) | |
#DSi only | |
mtu = f.read(1)[0] | (f.read(1)[0] << 8) | |
print('Wifi %d MTU: %d' % (slot_number, mtu)) | |
f.seek(offset + 0xc0) | |
ip_address = f.read(4) | |
if is_null_ip_address(ip_address): | |
print('Wifi %d IP address: DHCP' % slot_number) | |
else: | |
print('Wifi %d IP address: %s' % (slot_number, format_ip_address(ip_address))) | |
gateway = f.read(4) | |
print('Wifi %d gateway: %s' % (slot_number, format_ip_address(gateway))) | |
primary_dns = f.read(4) | |
print('Wifi %d primary: %s' % (slot_number, format_ip_address(primary_dns))) | |
secondary_dns = f.read(4) | |
print('Wifi %d secondary: %s' % (slot_number, format_ip_address(secondary_dns))) | |
subnet_mask = f.read(1)[0] | |
print('Wifi %d subnet mask: /%d' % (slot_number, subnet_mask)) | |
if wep_mode > 0: | |
f.seek(offset + 0x80) | |
wep_keys = [None] * 4 | |
for i in range(4): | |
wep_keys[i] = f.read(16) | |
if wep_mode == 5: | |
wep_keys[i] = wep_keys[i].decode('ascii', errors='backslashreplace')[:5] | |
if wep_mode == 6: | |
wep_keys[i] = wep_keys[i].decode('ascii', errors='backslashreplace')[:13] | |
if wep_mode == 7: | |
wep_keys[i] = wep_keys[i].decode('ascii', errors='backslashreplace')[:16] | |
#TODO Format properly when WEP mode is hex | |
print('Wifi %d WEP keys: %s' % (slot_number, list(wep_keys))) | |
def read_dsi_wifi_config(f, offset, slot_number): | |
f.seek(offset + 0xe7) | |
status = f.read(1)[0] | |
if status == 0xff: | |
return | |
print('Wifi %d status: %s' % (slot_number, WIFI_STATUSES.get(status, 'Unknown 0x{0:02X}'.format(status)))) | |
ssid_length = f.read(1)[0] | |
f.seek(offset + 0x40) | |
ssid = f.read(ssid_length).decode('ascii', errors='backslashreplace') | |
print('Wifi %d SSID: %s' % (slot_number, ssid)) | |
f.seek(offset + 0xe6) | |
wep_type = f.read(1)[0] | |
if wep_type != 0: | |
print('Wifi %d WEP type: %s' % (slot_number, wep_type)) | |
f.seek(offset + 0x182) | |
proxy_enabled = f.read(1)[0] > 0 | |
if proxy_enabled: | |
proxy_uses_auth = f.read(1)[0] > 0 | |
proxy_name = f.read(100).decode('ascii', errors='backslashreplace').rstrip('\0') | |
print('Wifi %d proxy name: %s' % (slot_number, proxy_name)) | |
proxy_port = f.read(1)[0] | (f.read(1)[0] << 8) | |
print('Wifi %d proxy port: %s' % (slot_number, proxy_port)) | |
if proxy_uses_auth: | |
f.seek(offset) | |
proxy_auth_username = f.read(32).decode('ascii', errors='backslashreplace').rstrip('\0') | |
proxy_auth_password = f.read(32).decode('ascii', errors='backslashreplace').rstrip('\0') | |
print('Wifi %d proxy authentication: username %s password %s' % (slot_number, proxy_auth_username, proxy_auth_password)) | |
f.seek(offset + 0xc0) | |
ip_address = f.read(4) | |
if is_null_ip_address(ip_address): | |
print('Wifi %d IP address: DHCP' % slot_number) | |
else: | |
print('Wifi %d IP address: %s' % (slot_number, format_ip_address(ip_address))) | |
gateway = f.read(4) | |
print('Wifi %d gateway: %s' % (slot_number, format_ip_address(gateway))) | |
primary_dns = f.read(4) | |
print('Wifi %d primary: %s' % (slot_number, format_ip_address(primary_dns))) | |
secondary_dns = f.read(4) | |
print('Wifi %d secondary: %s' % (slot_number, format_ip_address(secondary_dns))) | |
subnet_mask = f.read(1)[0] | |
print('Wifi %d subnet mask: /%d' % (slot_number, subnet_mask)) | |
if status == 16: | |
f.seek(offset + 0x120) | |
wpa_key = f.read(64).decode('ascii', errors='backslashreplace').rstrip('\0') | |
#GBATEK says this is only 16 bytes, but that is clearly wrong as WPA keys can be longer than that | |
print('Wifi %d WPA/WPA2 key: %s' % (slot_number, wpa_key)) | |
f.seek(offset + 0x181) | |
wpa_type = f.read(1)[0] | |
print('Wifi %d WPA type: %s' % (slot_number, WPA_TYPES.get(wpa_type, 'Unknown 0x{0:02X}'.format(wpa_type)))) | |
with open(sys.argv[1], 'rb') as f: | |
f.seek(8) | |
firmware_id = f.read(4) | |
print('Firmware ID: %s' % list(firmware_id)) | |
f.seek(0x18) | |
firmware_version = f.read(5) | |
#BCD minute hour day month year | |
#TODO Convert this | |
#TODO Can we get the fat DS firmware version from this? | |
print('Firmware build date: %s' % list(firmware_version)) | |
console_type = f.read(1)[0] | |
print('Console type: %s' % CONSOLE_TYPES.get(console_type, 'Unknown 0x{0:02X}'.format(console_type))) | |
f.seek(32) | |
user_settings_offset = f.read(2) | |
offset = ((user_settings_offset[1] << 8) | user_settings_offset[0]) * 8 | |
f.seek(offset) #I guess? | |
#It's always 5, unless something goes wrong | |
user_settings_version = f.read(2) | |
print('User settings version: %s' % list(user_settings_version)) | |
favourite_colour = f.read(1)[0] | |
print('Favourite colour: %s' % COLOURS.get(favourite_colour, 'Unknown 0x{0:02X}'.format(favourite_colour))) | |
birthday_month = f.read(1)[0] | |
birthday_year = f.read(1)[0] | |
print('Birthday: {0} {1}'.format(calendar.month_name[birthday_month], birthday_year)) | |
unused = f.read(1)[0] | |
print('Secret unused user setting: %s' % unused) | |
name = f.read(20).decode('utf-16le', errors='backslashreplace') | |
name_length = f.read(1)[0] | (f.read(1)[0] << 8) | |
print('Name: %s' % name[:name_length]) | |
message = f.read(52).decode('utf-16le', errors='backslashreplace') | |
message_length = f.read(1)[0] | (f.read(1)[0] << 8) | |
print('Message: %s' % message[:message_length]) | |
alarm_hour = f.read(1)[0] | |
alarm_minute = f.read(1)[0] | |
print('Alarm time: {0}:{1}'.format(alarm_hour, alarm_minute)) | |
#TODO Touch calibration and whatnot | |
f.seek(offset + 0x64) | |
flags = f.read(2) | |
language = flags[0] & 7 | |
print('Language: %s' % LANGUAGES.get(language)) | |
gba_screen = flags[0] & 8 | |
print('GBA screen: %s' % 'Upper' if gba_screen == 0 else 'Lower') | |
backlight_level = (flags[0] & 48) >> 4 #0 if not DS Lite | |
print('Backlight level: %d' % backlight_level) | |
#TODO other flags (cbf tbh m8) | |
f.seek(0x2f) | |
wifi_version = f.read(1)[0] | |
print('Wifi version: %d' % wifi_version) | |
read_wifi_config(f, offset - 0x400, 1) | |
read_wifi_config(f, offset - 0x300, 2) | |
read_wifi_config(f, offset - 0x200, 3) | |
if console_type == 0x57: | |
read_dsi_wifi_config(f, offset - 0xa00, 4) | |
read_dsi_wifi_config(f, offset - 0x800, 5) | |
read_dsi_wifi_config(f, offset - 0x600, 6) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment