Talk:SC Communication: Difference between revisions
m (formatting) |
m (Replaced imgur links) |
||
(50 intermediate revisions by 7 users not shown) | |||
Line 1: | Line 1: | ||
== The easy peasy lemon squeasy way (UART) (CXR/CXRF/SW) == | |||
{{boxcode|height=400px|title=SysconAUTH.py|code=<syntaxhighlight lang=python> | |||
# Python 2 + 3 compatible | |||
from binascii import unhexlify as uhx | |||
from Crypto.Cipher import AES # pycryptodome | |||
import os | |||
import serial # pyserial | |||
import string | |||
import sys | |||
import time | |||
class PS3UART(object): | |||
ser = serial.Serial() | |||
type = '' | |||
sc2tb = uhx('71f03f184c01c5ebc3f6a22a42ba9525') # Syscon to TestBench Key (0x130 xor 0x4578) | |||
tb2sc = uhx('907e730f4d4e0a0b7b75f030eb1d9d36') # TestBench to Syscon Key (0x130 xor 0x4588) | |||
value = uhx('3350BD7820345C29056A223BA220B323') # 0x45B8 | |||
zero = uhx('00000000000000000000000000000000') | |||
auth1r_header = uhx('10100000FFFFFFFF0000000000000000') | |||
auth2_header = uhx('10010000000000000000000000000000') | |||
def aes_decrypt_cbc(self, key, iv, in_data): | |||
return AES.new(key, AES.MODE_CBC, iv).decrypt(in_data) | |||
def aes_encrypt_cbc(self, key, iv, in_data): | |||
return AES.new(key, AES.MODE_CBC, iv).encrypt(in_data) | |||
def __init__(self, port, type): | |||
self.ser.port = port | |||
if(type == 'CXR' or type == 'SW'): | |||
self.ser.baudrate = 57600 | |||
elif(type == 'CXRF'): | |||
self.ser.baudrate = 115200 | |||
else: | |||
assert(False) | |||
self.type = type | |||
self.ser.timeout = 0.1 | |||
self.ser.open() | |||
assert(self.ser.isOpen()) | |||
self.ser.flush() | |||
def __del__(self): | |||
self.ser.close() | |||
def send(self, data): | |||
self.ser.write(data.encode('ascii')) | |||
def receive(self): | |||
return self.ser.read(self.ser.inWaiting()) | |||
def command(self, com, wait = 1, verbose = False): | |||
if(verbose): | |||
print('Command: ' + com) | |||
if(self.type == 'CXR'): | |||
length = len(com) | |||
checksum = sum(bytearray(com, 'ascii')) % 0x100 | |||
if(length <= 10): | |||
self.send('C:{:02X}:{}\r\n'.format(checksum, com)) | |||
else: | |||
j = 10 | |||
self.send('C:{:02X}:{}'.format(checksum, com[0:j])) | |||
for i in range(length - j, 15, -15): | |||
self.send(com[j:j+15]) | |||
j += 15 | |||
self.send(com[j:] + '\r\n') | |||
elif(self.type == 'SW'): | |||
length = len(com) | |||
if(length >= 0x40): | |||
if(self.command('SETCMDLONG FF FF')[0] != 0): | |||
return (0xFFFFFFFF, ['Setcmdlong']) | |||
checksum = sum(bytearray(com, 'ascii')) % 0x100 | |||
self.send('{}:{:02X}\r\n'.format(com, checksum)) | |||
else: | |||
self.send(com + '\r\n') | |||
time.sleep(wait) | |||
answer = self.receive().decode('ascii').strip() | |||
if(verbose): | |||
print('Answer: ' + answer) | |||
if(self.type == 'CXR'): | |||
answer = answer.split(':') | |||
if(len(answer) != 3): | |||
return (0xFFFFFFFF, ['Answer length']) | |||
checksum = sum(bytearray(answer[2], 'ascii')) % 0x100 | |||
if(answer[0] != 'R' and answer[0] != 'E'): | |||
return (0xFFFFFFFF, ['Magic']) | |||
if(answer[1] != '{:02X}'.format(checksum)): | |||
return (0xFFFFFFFF, ['Checksum']) | |||
data = answer[2].split(' ') | |||
if(answer[0] == 'R' and len(data) < 2 or answer[0] == 'E' and len(data) != 2): | |||
return (0xFFFFFFFF, ['Data length']) | |||
if(data[0] != 'OK' or len(data) < 2): | |||
return (int(data[1], 16), []) | |||
else: | |||
return (int(data[1], 16), data[2:]) | |||
elif(self.type == 'SW'): | |||
answer = answer.split('\n') | |||
for i in range(0, len(answer)): | |||
answer[i] = answer[i].replace('\n', '').rsplit(':', 1) | |||
if(len(answer[i]) != 2): | |||
return (0xFFFFFFFF, ['Answer length']) | |||
checksum = sum(bytearray(answer[i][0], 'ascii')) % 0x100 | |||
if(answer[i][1] != '{:02X}'.format(checksum)): | |||
return (0xFFFFFFFF, ['Checksum']) | |||
answer[i][0] += '\n' | |||
ret = answer[-1][0].replace('\n', '').split(' ') | |||
if(len(ret) < 2 or len(ret[1]) != 8 and not all(c in string.hexdigits for c in ret[1])): | |||
return (0, [x[0] for x in answer]) | |||
elif(len(answer) == 1): | |||
return (int(ret[1], 16), ret[2:]) | |||
else: | |||
return (int(ret[1], 16), [x[0] for x in answer[:-1]]) | |||
else: | |||
return (0, [answer]) | |||
def auth(self): | |||
if(self.type == 'CXR' or self.type == 'SW'): | |||
auth1r = self.command('AUTH1 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') | |||
if(auth1r[0] == 0 and auth1r[1] != []): | |||
auth1r = uhx(auth1r[1][0]) | |||
if(auth1r[0:0x10] == self.auth1r_header): | |||
data = self.aes_decrypt_cbc(self.sc2tb, self.zero, auth1r[0x10:0x40]) | |||
if(data[0x8:0x10] == self.zero[0x0:0x8] and data[0x10:0x20] == self.value and data[0x20:0x30] == self.zero): | |||
new_data = data[0x8:0x10] + data[0x0:0x8] + self.zero + self.zero | |||
auth2_body = self.aes_encrypt_cbc(self.tb2sc, self.zero, new_data) | |||
auth2r = self.command('AUTH2 ' + ''.join('{:02X}'.format(c) for c in bytearray(self.auth2_header + auth2_body))) | |||
if(auth2r[0] == 0): | |||
return 'Auth successful' | |||
else: | |||
return 'Auth failed' | |||
else: | |||
return 'Auth1 response body invalid' | |||
else: | |||
return 'Auth1 response header invalid' | |||
else: | |||
return 'Auth1 response invalid' | |||
else: | |||
scopen = self.command('scopen') | |||
if('SC_READY' in scopen[1][0]): | |||
auth1r = self.command('10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') | |||
auth1r = auth1r[1][0].split('\r')[1][1:] | |||
if(len(auth1r) == 128): | |||
auth1r = uhx(auth1r) | |||
if(auth1r[0:0x10] == self.auth1r_header): | |||
data = self.aes_decrypt_cbc(self.sc2tb, self.zero, auth1r[0x10:0x40]) | |||
if(data[0x8:0x10] == self.zero[0x0:0x8] and data[0x10:0x20] == self.value and data[0x20:0x30] == self.zero): | |||
new_data = data[0x8:0x10] + data[0x0:0x8] + self.zero + self.zero | |||
auth2_body = self.aes_encrypt_cbc(self.tb2sc, self.zero, new_data) | |||
auth2r = self.command(''.join('{:02X}'.format(c) for c in bytearray(self.auth2_header + auth2_body))) | |||
if('SC_SUCCESS' in auth2r[1][0]): | |||
return 'Auth successful' | |||
else: | |||
return 'Auth failed' | |||
else: | |||
return 'Auth1 response body invalid' | |||
else: | |||
return 'Auth1 response header invalid' | |||
else: | |||
return 'Auth1 response invalid' | |||
else: | |||
return 'scopen response invalid' | |||
def main(argc, argv): | |||
if(argc < 3): | |||
print(os.path.basename(__file__) + ' <serial port> <sc type ["CXR", "CXRF", "SW"]>') | |||
sys.exit(1) | |||
ps3 = PS3UART(argv[1], argv[2]) | |||
raw_input_c = vars(__builtins__).get('raw_input', input) | |||
while True: | |||
in_data = raw_input_c('> ') | |||
if(in_data.lower() == 'auth'): | |||
print(ps3.auth()) | |||
continue | |||
if(in_data.lower() == 'exit'): | |||
break | |||
ret = ps3.command(in_data) | |||
if(argv[2] == 'CXR'): | |||
print('{:08X}'.format(ret[0]) + ' ' + ' '.join(ret[1])) | |||
elif(argv[2] == 'SW'): | |||
if(len(ret[1]) > 0 and '\n' not in ret[1][0]): | |||
print('{:08X}'.format(ret[0]) + ' ' + ' '.join(ret[1])) | |||
else: | |||
print('{:08X}'.format(ret[0]) + '\n' + ''.join(ret[1])) | |||
else: | |||
print(ret[1][0]) | |||
if __name__ == '__main__': | |||
main(len(sys.argv), sys.argv) | |||
</syntaxhighlight>}} | |||
* Credits to M4j0r for the RE of this important info | |||
=== Example Scripts === | |||
{{boxcode|height=400px|title=SysconEEPdumpCXR.py|code=<syntaxhighlight lang=python> | |||
# Python 2 compatible | |||
if(len(sys.argv) < 3): | |||
print os.path.basename(__file__) + ' <serial port> <output file>' | |||
sys.exit(1) | |||
ps3 = PS3UART(sys.argv[1], 'CXR') | |||
print "Version: " + ps3.command("VER")[1][0] | |||
print ps3.auth() | |||
f = open(sys.argv[2], 'wb') | |||
block_size = 0x40 | |||
print "Dumping NVS" | |||
failed = [] | |||
for i in xrange(0x2C00, 0x7400, block_size): # 0x7400 for CXR713, 0x4400 for CXR714 | |||
print "Reading 0x{:04X}".format(i) | |||
data = ps3.command("R8 {:08X} {:02X}".format(i, block_size)) | |||
ret = data[0] | |||
if ret == 0: | |||
f.write((data[1][0]).decode('hex')) | |||
else: | |||
print "Failed: " + str(ret) | |||
failed += [i] | |||
f.write(("A"*block_size*2).decode('hex')) | |||
f.close() | |||
time.sleep(2) | |||
print "\nRetrying failed offsets" | |||
for i in failed: | |||
print "Reading 0x{:04X}".format(i) | |||
for j in xrange(0, block_size, block_size/4): | |||
while True: | |||
data = ps3.command("R8 {:08X} {:02X}".format(i+j, block_size/4)) | |||
ret = data[0] | |||
if ret == 0: | |||
print data[1][0] | |||
break | |||
time.sleep(2) | |||
</syntaxhighlight>}} | |||
{{boxcode|height=400px|title=SysconEEPdumpCXRF.py|code=<syntaxhighlight lang=python> | |||
# Python 2 + 3 compatible | |||
if(len(sys.argv) < 3): | |||
print(os.path.basename(__file__) + ' <serial port> <output file>') | |||
sys.exit(1) | |||
ps3 = PS3UART(sys.argv[1], 'CXRF') | |||
print('Version: ' + ps3.command('version')[1][0].split('\n')[1]) | |||
print(ps3.auth()) | |||
f = open(sys.argv[2], 'wb') | |||
block_size = 0x40 | |||
print('Dumping NVS') | |||
for i in range(0x2C00, 0x7400, block_size): | |||
print('Reading 0x{:04X}'.format(i)) | |||
data = ps3.command('r {:08X} {:02X}'.format(i, block_size)) | |||
data = data[1][0].split('\n') | |||
if len(data) != 9: | |||
print('Failed') | |||
continue | |||
for i in range(3, 7): | |||
temp = data[i][0:-2].replace(' ', '') | |||
f.write(bytearray.fromhex(temp)) | |||
f.close() | |||
</syntaxhighlight>}} | |||
{{boxcode|height=400px|title=SysconEEPdumpSW.py|code=<syntaxhighlight lang=python> | |||
# Python 2 + 3 compatible | |||
if(len(sys.argv) < 3): | |||
print(os.path.basename(__file__) + ' <serial port> <output file>') | |||
sys.exit(1) | |||
ps3 = PS3UART(sys.argv[1], 'SW') | |||
print('Version: ' + ps3.command('VER')[1][0]) | |||
print(ps3.auth()) | |||
f = open(sys.argv[2], 'wb') | |||
block_size = 0x40 | |||
print('Dumping NVS') | |||
for i in range(0x0, 0x1400, block_size): | |||
print('Reading 0x{:04X}'.format(i)) | |||
data = ps3.command('EEP GET {:08X} {:02X}'.format(i, block_size)) | |||
ret = data[0] | |||
temp = '' | |||
if ret == 0: | |||
for i in range(2, len(data[1])): | |||
temp += data[1][i][2:-2].replace(' ', '') | |||
f.write(bytearray.fromhex(temp)) | |||
else: | |||
print('Failed: ' + str(ret)) | |||
f.close() | |||
</syntaxhighlight>}} | |||
{{boxcode|height=400px|title=SysconEEPpatchCXR.py|code=<syntaxhighlight lang=python> | |||
# Python 2 compatible | |||
if(len(sys.argv) < 3): | |||
print os.path.basename(__file__) + ' <serial port> <patch file>' | |||
sys.exit(1) | |||
ps3 = PS3UART(sys.argv[1], 'CXR') | |||
f = open(sys.argv[2], 'rb') | |||
patch = f.read() | |||
f.close() | |||
print "Version: " + ps3.command("VER")[1][0] | |||
print ps3.auth() | |||
patch_area_1 = 0x2800 | |||
patch_area_2 = 0x4400 # 0x7400 for CXR713, 0x4400 for CXR714 | |||
block_size = 0x40 | |||
print "First region" | |||
for i in xrange(0, 0x400, block_size): | |||
print hex(ps3.command("EEP SET " + hex(i+patch_area_1)[2:] + " " + hex(block_size)[2:] + " " + patch[i:i+block_size].encode('hex'))[0]) | |||
print "" | |||
print "Second region" | |||
for i in xrange(0x400, 0x1000, block_size): | |||
print hex(ps3.command("EEP SET " + hex(i+patch_area_2-0x400)[2:] + " " + hex(block_size)[2:] + " " + patch[i:i+block_size].encode('hex'))[0]) | |||
</syntaxhighlight>}} | |||
== IDs == | |||
<pre> | |||
//phase ids | |||
#define PHASE_ID_GET_STATE 0x0 | |||
#define PHASE_ID_PERSONALIZE 0x1 | |||
#define PHASE_ID_AUTH1 0x2 | |||
#define PHASE_ID_AUTH2 0x3 | |||
#define PHASE_ID_SEQ_SERVICE 0x4 | |||
#define PHASE_ID_GET_PERSONALIZE_VERSION 0x5 | |||
#define PHASE_ID_GET_SYSTEM_INFO 0x6 | |||
#define PHASE_ID_FACTORY_INIT 0xFF | |||
//seq service ids | |||
#define SEQ_SERVICE_ID_GET_RTC 0x1 | |||
#define SEQ_SERVICE_ID_SET_CLOCK 0x2 | |||
#define SEQ_SERVICE_ID_GET_CLOCK 0x3 | |||
#define SEQ_SERVICE_ID_READ_DATA 0x4 | |||
#define SEQ_SERVICE_ID_WRITE_DATA 0x5 | |||
#define SEQ_SERVICE_ID_GET_RTC_STATUS 0x6 | |||
#define SEQ_SERVICE_ID_SET_RTC_STATUS 0x7 | |||
#define SEQ_SERVICE_ID_SET_RTC 0x8 | |||
#define SEQ_SERVICE_ID_CORRECT_RTC 0x9 | |||
#define SEQ_SERVICE_ID_SET_RTC2 0xA | |||
#define SEQ_SERVICE_ID_CORRECT_RTC2 0xB | |||
</pre> | |||
== Syscon packets == | |||
=== Device Access Service (0x03) === | |||
==== EEPROM write ==== | |||
Purpose: Write block of data to EEPROM. | |||
<pre> | |||
struct __attribute__ ((packed)) dev_access_request_t { | |||
uint8_t cmd; // 0x01 | |||
uint8_t padding[3]; | |||
uint32_t offset; | |||
uint32_t size; | |||
uint8_t data[0]; | |||
}; | |||
struct __attribute__ ((packed)) dev_access_response_t { | |||
uint8_t status; // 0x00:OK, 0x03:Wrong offset, 0x04:Wrong size, 0xFF:Error | |||
uint8_t data[0]; | |||
}; | |||
</pre> | |||
* Although this service is not used on Slim and some Phat consoles anymore, it is working (only on consoles with a CXR (Mullion) Syscon). | |||
* It seems it have the same restrictions as NVS service. | |||
==== EEPROM read ==== | |||
Purpose: Read block of data from EEPROM. | |||
<pre> | |||
struct __attribute__ ((packed)) dev_access_request_t { | |||
uint8_t cmd; // 0x00 | |||
uint8_t padding[3]; | |||
uint32_t offset; | |||
uint32_t size; | |||
}; | |||
struct __attribute__ ((packed)) dev_access_response_t { | |||
uint8_t status; // 0x00:OK, 0x03:Wrong offset, 0x04:Wrong size, 0xFF:Error | |||
uint8_t data[0]; | |||
}; | |||
</pre> | |||
* Although this service is not used on Slim and some Phat consoles anymore, it is working (only on consoles with a CXR (Mullion) Syscon). | |||
* It seems it have the same restrictions as NVS service. | |||
=== Thermal Service (0x11) === | |||
==== Get temperature alert? ==== | |||
Purpose: Used to get current temperature alert?. | |||
<pre> | |||
struct __attribute__ ((packed)) get_thermal_alert_request_t { | |||
uint8_t cmd; // 0x20 | |||
uint8_t param; // 0xFF | |||
}; | |||
struct __attribute__ ((packed)) get_thermal_alert_response_t { | |||
uint8_t unk1; // status? | |||
uint8_t unk2; // status? | |||
uint8_t data[32]; | |||
}; | |||
</pre> | |||
==== Get temperature ==== | |||
Purpose: Used to get current temperature. | |||
<pre> | |||
struct __attribute__ ((packed)) get_temperature_request_t { | |||
uint8_t cmd; // 0x00 | |||
uint8_t tzone; // 0x00:CELL, 0x01:RSX | |||
}; | |||
struct __attribute__ ((packed)) get_temperature_response_t { | |||
uint8_t status; | |||
int8_t temperature_hi; | |||
int8_t temperature_lo; | |||
}; | |||
</pre> | |||
* Temperature in celsius: sprintf("%d.%d", temperature_hi, (temperature_lo * 100) / 256); | |||
=== Configuration Service (0x12) === | |||
==== Get XDR configuration ==== | |||
Purpose: Used at bootloader during initialization. | |||
<pre> | |||
struct __attribute__ ((packed)) get_xdr_config_request_t { | |||
uint8_t cmd; // 0x00 | |||
uint8_t param; // 0x00 | |||
}; | |||
struct __attribute__ ((packed)) get_xdr_config_response_t { | |||
uint8_t cmd; | |||
uint8_t padding[3]; | |||
uint8_t data[128]; | |||
}; | |||
</pre> | |||
<pre> | |||
struct __attribute__ ((packed)) get_xdr_config2_request_t { | |||
uint8_t cmd; // 0x01 | |||
uint8_t param; // 0x00 | |||
}; | |||
struct __attribute__ ((packed)) get_xdr_config2_response_t { | |||
uint8_t cmd; | |||
uint8_t data_size?; | |||
uint8_t padding[2]; | |||
uint8_t data[256]; | |||
}; | |||
</pre> | |||
* get_xdr_config2 returns more data (maybe different config?) | |||
==== Get IDlog information ==== | |||
Purpose: Can be used to get CID/eCID information. | |||
<pre> | |||
struct __attribute__ ((packed)) get_idlog_info_request_t { | |||
uint8_t cmd; // 0x03 | |||
uint8_t param; // 0x01 | |||
}; | |||
struct __attribute__ ((packed)) get_idlog_info_response_t { | |||
uint8_t cmd; | |||
uint8_t padding[3]; | |||
uint8_t ecid[16]; | |||
uint8_t cid[6]; | |||
}; | |||
</pre> | |||
==== sc_config_info::be::get_reference_clock ==== | |||
Purpose: Used to calculate an initial value of timebase register. | |||
<pre> | |||
struct __attribute__ ((packed)) get_reference_clock_request_t { | |||
uint8_t cmd; // 0x03 | |||
uint8_t param; // 0x10 | |||
}; | |||
struct __attribute__ ((packed)) get_reference_clock_response_t { | |||
uint8_t cmd; | |||
uint8_t padding[3]; | |||
uint32_t ref_clock_value; | |||
uint32_t unk; | |||
}; | |||
</pre> | |||
==== sc_config_info::xdr::get_reference_clock ==== | |||
<pre> | |||
struct __attribute__ ((packed)) get_reference_clock_request_t { | |||
uint8_t cmd; // 0x00 | |||
uint8_t param; // 0x10 | |||
}; | |||
struct __attribute__ ((packed)) get_reference_clock_response_t { | |||
uint8_t cmd; | |||
uint8_t padding[3]; | |||
uint32_t ref_clock_value; | |||
uint32_t unk; | |||
}; | |||
</pre> | |||
==== Get core clock multiplier ==== | |||
Purpose: Used together with reference clock to calculate clock frequency. | |||
<pre> | |||
struct __attribute__ ((packed)) get_core_clock_multiplier_request_t { | |||
uint8_t cmd; // 0x03 | |||
uint8_t param; // 0x00 | |||
}; | |||
struct __attribute__ ((packed)) get_core_clock_multiplier_response_t { | |||
uint8_t cmd; | |||
uint8_t padding[3]; | |||
uint8_t id; // 00:2, 01:4, 03: 8, 04:10, 05:12, 06:16, 07:20 | |||
}; | |||
</pre> | |||
* Clock frequency = core clock multiplier table[id] * reference clock. | |||
* For example, if you have id=3 and reference clock=400000000, then your clock frequency will be: 400000000*8=3200000000 (3.2 Ghz) | |||
==== Get platform ID ==== | |||
Purpose: Used to get platform ID (Cytology, Cookie, etc). | |||
<pre> | |||
struct __attribute__ ((packed)) get_platform_id_request_t { | |||
uint8_t cmd; // 0x20 | |||
uint8_t param; // 0x10 | |||
}; | |||
struct __attribute__ ((packed)) get_platform_id_response_t { | |||
uint8_t cmd; | |||
uint8_t padding[3]; | |||
uint8_t platform_id[8]; // ASCII-string | |||
}; | |||
</pre> | |||
==== Get hardware configuration ==== | |||
Purpose: Used to get hardware configuration (PS2 emulation compatibility, card support, WLAN interface, etc). | |||
<pre> | |||
struct __attribute__ ((packed)) get_hw_config_request_t { | |||
uint8_t cmd; // 0x22 | |||
}; | |||
struct __attribute__ ((packed)) get_hw_config_response_t { | |||
uint8_t cmd; | |||
uint8_t padding[3]; | |||
uint8_t data[12]; | |||
}; | |||
</pre> | |||
==== Get BE counters ==== | |||
Purpose: Used to get several BE counters. | |||
<pre> | |||
struct __attribute__ ((packed)) get_be_counters_request_t { | |||
uint8_t cmd; // 0x41 | |||
}; | |||
struct __attribute__ ((packed)) get_be_counters_response_t { | |||
uint8_t cmd; | |||
uint32_t total_operation_time; | |||
uint32_t power_on_counter; | |||
uint32_t power_off_counter; | |||
}; | |||
</pre> | |||
=== Power Service (0x13) === | |||
==== Shutdown ==== | |||
Purpose: Shutdown console. | |||
<pre> | |||
struct __attribute__ ((packed)) shutdown_request_t { | |||
uint8_t cmd; // 0x00 | |||
}; | |||
</pre> | |||
==== Reboot ==== | |||
Purpose: Reboot console. | |||
<pre> | |||
struct __attribute__ ((packed)) reboot_request_t { | |||
uint8_t cmd; // 0x01 | |||
}; | |||
</pre> | |||
==== Query system power up cause ==== | |||
Purpose: Get information about system power up cause. | |||
<pre> | |||
struct __attribute__ ((packed)) query_system_power_up_cause_request_t { | |||
uint8_t cmd; // 0x10 | |||
}; | |||
struct __attribute__ ((packed)) query_system_power_up_cause_response_t { | |||
uint8_t cmd; | |||
uint8_t padding1[3]; | |||
uint32_t wake_source; | |||
uint8_t requested_os_context; | |||
uint8_t current_os_context; | |||
uint8_t requested_gr_context; | |||
uint8_t current_gr_context; | |||
uint8_t last_shutdown_cause; | |||
uint8_t padding2[3]; | |||
}; | |||
</pre> | |||
==== Get realtime clock ==== | |||
Purpose: Get current value of realtime clock. | |||
<pre> | |||
struct __attribute__ ((packed)) get_rtc_request_t { | |||
uint8_t cmd; // 0x33 | |||
}; | |||
struct __attribute__ ((packed)) get_rtc_response_t { | |||
uint8_t cmd; | |||
uint8_t padding[3]; | |||
uint32_t rtc; | |||
}; | |||
</pre> | |||
=== NVS Service (0x14) === | |||
==== EEPROM write ==== | |||
Purpose: Write block of data to EEPROM. | |||
<pre> | |||
struct __attribute__ ((packed)) nvs_access_request_t { | |||
uint8_t cmd; // 0x10 | |||
uint8_t index; | |||
uint8_t offset; | |||
uint8_t size; // 0x00 for full block | |||
uint8_t data[0]; | |||
}; | |||
struct __attribute__ ((packed)) nvs_access_response_t { | |||
uint8_t status; // 0x00:OK, 0x02:Invalid block index, 0x03:Invalid offset/size | |||
uint8_t index; | |||
uint8_t offset; | |||
uint8_t size; | |||
uint8_t data[0]; | |||
}; | |||
</pre> | |||
==== EEPROM read ==== | |||
Purpose: Read block of data from EEPROM. | |||
<pre> | |||
struct __attribute__ ((packed)) nvs_access_request_t { | |||
uint8_t cmd; // 0x20 | |||
uint8_t index; | |||
uint8_t offset; | |||
uint8_t size; // 0x00 for full block | |||
}; | |||
struct __attribute__ ((packed)) nvs_access_response_t { | |||
uint8_t status; // 0x00:OK, 0x02:Invalid block index, 0x03:Invalid offset/size | |||
uint8_t index; | |||
uint8_t offset; | |||
uint8_t size; | |||
uint8_t data[0]; | |||
}; | |||
</pre> | |||
=== Version Service (0x18) === | |||
==== Get service version ==== | |||
Purpose: Get information about service version. | |||
<pre> | |||
struct __attribute__ ((packed)) get_service_version_request_t { | |||
uint8_t cmd; // 0x01 | |||
uint8_t service_id; | |||
}; | |||
struct __attribute__ ((packed)) get_service_version_response_t { | |||
uint8_t status; | |||
uint8_t service_id; | |||
uint8_t major_version; | |||
uint8_t minor_version; | |||
}; | |||
</pre> | |||
==== Get syscon version ==== | |||
Purpose: Get information about syscon version.<br> | |||
Note: Syscon Auth required. | |||
<pre> | |||
struct __attribute__ ((packed)) get_sc_version_request_t { | |||
uint8_t cmd; // 0x12/0x14 | |||
}; | |||
struct __attribute__ ((packed)) get_sc_version_response_t { | |||
uint8_t cmd; | |||
uint8_t padding[3]; | |||
uint16_t version; | |||
}; | |||
</pre> | |||
=== Livelock Service (0x1B) === | |||
==== Set livelock detection mode ==== | |||
<pre> | |||
struct __attribute__ ((packed)) set_livelock_detection_mode_request_t { | |||
uint8_t cmd; // 0x10 | |||
uint8_t param; //0x00 | |||
}; | |||
struct __attribute__ ((packed)) set_livelock_detection_mode_response_t { | |||
uint8_t data; // | |||
}; | |||
</pre> | |||
=== A/V Service (0x30) === | |||
==== Get monitor information ==== | |||
Purpose: Get monitor information (EDID). | |||
<pre> | |||
struct __attribute__ ((packed)) get_monitor_info_request_t { | |||
uint8_t cmd; // 0x0D | |||
uint8_t av_port; // 0x00 | |||
uint8_t unk1; // 0x00 | |||
uint8_t unk2; // 0x00 (garbage) | |||
}; | |||
struct __attribute__ ((packed)) get_monitor_info_response_t { | |||
uint8_t status; | |||
uint8_t unk1; | |||
uint8_t unk2; | |||
uint8_t unk3; | |||
uint8_t data[256]; | |||
}; | |||
</pre> | |||
Sample responses when HDMI inserted (personal info removed): | |||
<pre> | <pre> | ||
000c000: 3001 0000 0000 8031 8000 0000 0104 0104 0......1........ | 000c000: 3001 0000 0000 8031 8000 0000 0104 0104 0......1........ | ||
Line 20: | Line 748: | ||
000c100: 1620 582c 2500 132b 2100 009f 0000 0000 . X,%..+!....... | 000c100: 1620 582c 2500 132b 2100 009f 0000 0000 . X,%..+!....... | ||
</pre> | </pre> | ||
<pre> | |||
0000h: 00 FF FF FF FF FF FF 00 XX XX XX XX XX XX XX XX .яяяяяя......... | |||
0010h: 26 17 01 03 80 3C 22 78 2E 2F A5 A5 54 50 9E 27 &...Ђ<"x./ҐҐTPћ' | |||
0020h: 10 50 54 A5 6B 80 D1 C0 61 C0 81 00 81 C0 81 40 .PTҐkЂСАaАЃ.ЃАЃ@ | |||
0030h: 81 80 A9 C0 B3 00 02 3A 80 18 71 38 2D 40 58 2C ЃЂ©Аі..:Ђ.q8-@X, | |||
0040h: 45 00 56 50 21 00 00 1E 00 00 00 FF 00 41 39 44 E.VP!......я.A9D | |||
0050h: 30 30 36 31 30 53 4C 30 0A 20 00 00 00 FD 00 32 00610SL0. ...э.2 | |||
0060h: 4C 1E 53 15 00 0A 20 20 20 20 20 20 00 00 00 FC L.S... ...ь | |||
0070h: 00 XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX ................ | |||
0080h: 02 03 24 F1 4F 01 02 03 04 05 06 07 10 11 12 13 ..$сO........... | |||
0090h: 14 15 16 1F 23 09 07 07 83 01 00 00 67 03 0C 00 ....#...ѓ...g... | |||
00A0h: 10 00 00 2D 02 3A 80 18 71 38 2D 40 58 2C 45 00 ...-.:Ђ.q8-@X,E. | |||
00B0h: 56 50 21 00 00 1F 01 1D 80 18 71 1C 16 20 58 2C VP!.....Ђ.q.. X, | |||
00C0h: 25 00 56 50 21 00 00 9F 01 1D 00 72 51 D0 1E 20 %.VP!..џ...rQР. | |||
00D0h: 6E 28 55 00 56 50 21 00 00 1E 8C 0A D0 8A 20 E0 n(U.VP!...Њ.РЉ а | |||
00E0h: 2D 10 10 3E 96 00 56 50 21 00 00 18 00 00 00 00 -..>–.VP!....... | |||
00F0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 ...............– | |||
</pre> | |||
==== Get HDMI KSV ==== | |||
Purpose: Get HDMI key selection vector. | |||
<pre> | |||
struct __attribute__ ((packed)) get_hdmi_ksv_request_t { | |||
uint8_t cmd; // 0x11 | |||
}; | |||
struct __attribute__ ((packed)) get_hdmi_ksv_response_t { | |||
uint8_t result; | |||
uint8_t padding[3]; | |||
uint8_t ksv[5]; | |||
}; | |||
</pre> | |||
==Tools== | |||
*SysCommParser by TizzyT http://tizzyt-archive.blogspot.com.es/2015/02/syscommparser.html | |||
==Log Info== | |||
Each of the times a request is made that involves Auth1+Auth2,there is a communication made to eeprom for read/write. It goes like this (case for backup_srh): | |||
* Read initial 16 bytes of eeprom once (packet 0) (0x10) | |||
* Read 32 bytes following 20 times (packets 1-21) (0x280) | |||
* Auth1 established | |||
* Repeat steps 1 2 | |||
* Auth2 established | |||
* Repeat steps 1 2 for finding key?/validation? | |||
* Validation/Finding Key established | |||
* Final Packet(s): Read EEPROM Location/Write to EEPROM Location/Do Other Tasks | |||
Notes: | |||
* Initial bytes are constant and never change from phat to phat | |||
* Following bytes are perconsole (EID1 most likely) | |||
* Steps that don't involve sc auth1 auth2 such as reading eeprom using nvs service or checking syscon status (probably stored in RAM, because true value is in EEPROM encrypted) have no packet activity | |||
* initial bytes value: 99 D9 66 2B B3 D7 61 54 6B 9C 3F 9E D1 40 ED B0 | |||
* we haven't tested bootldr init yet, but we think it might be different from the others... | |||
Credits to [[User:Zer0Tolerance|ZeroTolerance]] for the info | |||
=== Log === | |||
http://pastie.org/private/0pnjuk1hp3qybqdjtpryq | |||
* KickStart Value (packet 0): 1.8480055 seconds | |||
* KickEnd Value : (packet 69): 5.8694735 seconds | |||
* Diff : 4.021468 seconds | |||
= LiveLock and Versioning Confusion = | |||
See : | |||
* https://www.psdevwiki.com/ps3/File:C1kVeMD.png | |||
* https://www.psdevwiki.com/ps3/File:NBkrqqZ.png | |||
* database : https://www.sendspace.com/file/6awfi9 | |||
For proof |
Latest revision as of 05:08, 5 May 2023
The easy peasy lemon squeasy way (UART) (CXR/CXRF/SW)[edit source]
# Python 2 + 3 compatible from binascii import unhexlify as uhx from Crypto.Cipher import AES # pycryptodome import os import serial # pyserial import string import sys import time class PS3UART(object): ser = serial.Serial() type = '' sc2tb = uhx('71f03f184c01c5ebc3f6a22a42ba9525') # Syscon to TestBench Key (0x130 xor 0x4578) tb2sc = uhx('907e730f4d4e0a0b7b75f030eb1d9d36') # TestBench to Syscon Key (0x130 xor 0x4588) value = uhx('3350BD7820345C29056A223BA220B323') # 0x45B8 zero = uhx('00000000000000000000000000000000') auth1r_header = uhx('10100000FFFFFFFF0000000000000000') auth2_header = uhx('10010000000000000000000000000000') def aes_decrypt_cbc(self, key, iv, in_data): return AES.new(key, AES.MODE_CBC, iv).decrypt(in_data) def aes_encrypt_cbc(self, key, iv, in_data): return AES.new(key, AES.MODE_CBC, iv).encrypt(in_data) def __init__(self, port, type): self.ser.port = port if(type == 'CXR' or type == 'SW'): self.ser.baudrate = 57600 elif(type == 'CXRF'): self.ser.baudrate = 115200 else: assert(False) self.type = type self.ser.timeout = 0.1 self.ser.open() assert(self.ser.isOpen()) self.ser.flush() def __del__(self): self.ser.close() def send(self, data): self.ser.write(data.encode('ascii')) def receive(self): return self.ser.read(self.ser.inWaiting()) def command(self, com, wait = 1, verbose = False): if(verbose): print('Command: ' + com) if(self.type == 'CXR'): length = len(com) checksum = sum(bytearray(com, 'ascii')) % 0x100 if(length <= 10): self.send('C:{:02X}:{}\r\n'.format(checksum, com)) else: j = 10 self.send('C:{:02X}:{}'.format(checksum, com[0:j])) for i in range(length - j, 15, -15): self.send(com[j:j+15]) j += 15 self.send(com[j:] + '\r\n') elif(self.type == 'SW'): length = len(com) if(length >= 0x40): if(self.command('SETCMDLONG FF FF')[0] != 0): return (0xFFFFFFFF, ['Setcmdlong']) checksum = sum(bytearray(com, 'ascii')) % 0x100 self.send('{}:{:02X}\r\n'.format(com, checksum)) else: self.send(com + '\r\n') time.sleep(wait) answer = self.receive().decode('ascii').strip() if(verbose): print('Answer: ' + answer) if(self.type == 'CXR'): answer = answer.split(':') if(len(answer) != 3): return (0xFFFFFFFF, ['Answer length']) checksum = sum(bytearray(answer[2], 'ascii')) % 0x100 if(answer[0] != 'R' and answer[0] != 'E'): return (0xFFFFFFFF, ['Magic']) if(answer[1] != '{:02X}'.format(checksum)): return (0xFFFFFFFF, ['Checksum']) data = answer[2].split(' ') if(answer[0] == 'R' and len(data) < 2 or answer[0] == 'E' and len(data) != 2): return (0xFFFFFFFF, ['Data length']) if(data[0] != 'OK' or len(data) < 2): return (int(data[1], 16), []) else: return (int(data[1], 16), data[2:]) elif(self.type == 'SW'): answer = answer.split('\n') for i in range(0, len(answer)): answer[i] = answer[i].replace('\n', '').rsplit(':', 1) if(len(answer[i]) != 2): return (0xFFFFFFFF, ['Answer length']) checksum = sum(bytearray(answer[i][0], 'ascii')) % 0x100 if(answer[i][1] != '{:02X}'.format(checksum)): return (0xFFFFFFFF, ['Checksum']) answer[i][0] += '\n' ret = answer[-1][0].replace('\n', '').split(' ') if(len(ret) < 2 or len(ret[1]) != 8 and not all(c in string.hexdigits for c in ret[1])): return (0, [x[0] for x in answer]) elif(len(answer) == 1): return (int(ret[1], 16), ret[2:]) else: return (int(ret[1], 16), [x[0] for x in answer[:-1]]) else: return (0, [answer]) def auth(self): if(self.type == 'CXR' or self.type == 'SW'): auth1r = self.command('AUTH1 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') if(auth1r[0] == 0 and auth1r[1] != []): auth1r = uhx(auth1r[1][0]) if(auth1r[0:0x10] == self.auth1r_header): data = self.aes_decrypt_cbc(self.sc2tb, self.zero, auth1r[0x10:0x40]) if(data[0x8:0x10] == self.zero[0x0:0x8] and data[0x10:0x20] == self.value and data[0x20:0x30] == self.zero): new_data = data[0x8:0x10] + data[0x0:0x8] + self.zero + self.zero auth2_body = self.aes_encrypt_cbc(self.tb2sc, self.zero, new_data) auth2r = self.command('AUTH2 ' + ''.join('{:02X}'.format(c) for c in bytearray(self.auth2_header + auth2_body))) if(auth2r[0] == 0): return 'Auth successful' else: return 'Auth failed' else: return 'Auth1 response body invalid' else: return 'Auth1 response header invalid' else: return 'Auth1 response invalid' else: scopen = self.command('scopen') if('SC_READY' in scopen[1][0]): auth1r = self.command('10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') auth1r = auth1r[1][0].split('\r')[1][1:] if(len(auth1r) == 128): auth1r = uhx(auth1r) if(auth1r[0:0x10] == self.auth1r_header): data = self.aes_decrypt_cbc(self.sc2tb, self.zero, auth1r[0x10:0x40]) if(data[0x8:0x10] == self.zero[0x0:0x8] and data[0x10:0x20] == self.value and data[0x20:0x30] == self.zero): new_data = data[0x8:0x10] + data[0x0:0x8] + self.zero + self.zero auth2_body = self.aes_encrypt_cbc(self.tb2sc, self.zero, new_data) auth2r = self.command(''.join('{:02X}'.format(c) for c in bytearray(self.auth2_header + auth2_body))) if('SC_SUCCESS' in auth2r[1][0]): return 'Auth successful' else: return 'Auth failed' else: return 'Auth1 response body invalid' else: return 'Auth1 response header invalid' else: return 'Auth1 response invalid' else: return 'scopen response invalid' def main(argc, argv): if(argc < 3): print(os.path.basename(__file__) + ' <serial port> <sc type ["CXR", "CXRF", "SW"]>') sys.exit(1) ps3 = PS3UART(argv[1], argv[2]) raw_input_c = vars(__builtins__).get('raw_input', input) while True: in_data = raw_input_c('> ') if(in_data.lower() == 'auth'): print(ps3.auth()) continue if(in_data.lower() == 'exit'): break ret = ps3.command(in_data) if(argv[2] == 'CXR'): print('{:08X}'.format(ret[0]) + ' ' + ' '.join(ret[1])) elif(argv[2] == 'SW'): if(len(ret[1]) > 0 and '\n' not in ret[1][0]): print('{:08X}'.format(ret[0]) + ' ' + ' '.join(ret[1])) else: print('{:08X}'.format(ret[0]) + '\n' + ''.join(ret[1])) else: print(ret[1][0]) if __name__ == '__main__': main(len(sys.argv), sys.argv)
- Credits to M4j0r for the RE of this important info
Example Scripts[edit source]
# Python 2 compatible if(len(sys.argv) < 3): print os.path.basename(__file__) + ' <serial port> <output file>' sys.exit(1) ps3 = PS3UART(sys.argv[1], 'CXR') print "Version: " + ps3.command("VER")[1][0] print ps3.auth() f = open(sys.argv[2], 'wb') block_size = 0x40 print "Dumping NVS" failed = [] for i in xrange(0x2C00, 0x7400, block_size): # 0x7400 for CXR713, 0x4400 for CXR714 print "Reading 0x{:04X}".format(i) data = ps3.command("R8 {:08X} {:02X}".format(i, block_size)) ret = data[0] if ret == 0: f.write((data[1][0]).decode('hex')) else: print "Failed: " + str(ret) failed += [i] f.write(("A"*block_size*2).decode('hex')) f.close() time.sleep(2) print "\nRetrying failed offsets" for i in failed: print "Reading 0x{:04X}".format(i) for j in xrange(0, block_size, block_size/4): while True: data = ps3.command("R8 {:08X} {:02X}".format(i+j, block_size/4)) ret = data[0] if ret == 0: print data[1][0] break time.sleep(2)
# Python 2 + 3 compatible if(len(sys.argv) < 3): print(os.path.basename(__file__) + ' <serial port> <output file>') sys.exit(1) ps3 = PS3UART(sys.argv[1], 'CXRF') print('Version: ' + ps3.command('version')[1][0].split('\n')[1]) print(ps3.auth()) f = open(sys.argv[2], 'wb') block_size = 0x40 print('Dumping NVS') for i in range(0x2C00, 0x7400, block_size): print('Reading 0x{:04X}'.format(i)) data = ps3.command('r {:08X} {:02X}'.format(i, block_size)) data = data[1][0].split('\n') if len(data) != 9: print('Failed') continue for i in range(3, 7): temp = data[i][0:-2].replace(' ', '') f.write(bytearray.fromhex(temp)) f.close()
# Python 2 + 3 compatible if(len(sys.argv) < 3): print(os.path.basename(__file__) + ' <serial port> <output file>') sys.exit(1) ps3 = PS3UART(sys.argv[1], 'SW') print('Version: ' + ps3.command('VER')[1][0]) print(ps3.auth()) f = open(sys.argv[2], 'wb') block_size = 0x40 print('Dumping NVS') for i in range(0x0, 0x1400, block_size): print('Reading 0x{:04X}'.format(i)) data = ps3.command('EEP GET {:08X} {:02X}'.format(i, block_size)) ret = data[0] temp = '' if ret == 0: for i in range(2, len(data[1])): temp += data[1][i][2:-2].replace(' ', '') f.write(bytearray.fromhex(temp)) else: print('Failed: ' + str(ret)) f.close()
# Python 2 compatible if(len(sys.argv) < 3): print os.path.basename(__file__) + ' <serial port> <patch file>' sys.exit(1) ps3 = PS3UART(sys.argv[1], 'CXR') f = open(sys.argv[2], 'rb') patch = f.read() f.close() print "Version: " + ps3.command("VER")[1][0] print ps3.auth() patch_area_1 = 0x2800 patch_area_2 = 0x4400 # 0x7400 for CXR713, 0x4400 for CXR714 block_size = 0x40 print "First region" for i in xrange(0, 0x400, block_size): print hex(ps3.command("EEP SET " + hex(i+patch_area_1)[2:] + " " + hex(block_size)[2:] + " " + patch[i:i+block_size].encode('hex'))[0]) print "" print "Second region" for i in xrange(0x400, 0x1000, block_size): print hex(ps3.command("EEP SET " + hex(i+patch_area_2-0x400)[2:] + " " + hex(block_size)[2:] + " " + patch[i:i+block_size].encode('hex'))[0])
IDs[edit source]
//phase ids #define PHASE_ID_GET_STATE 0x0 #define PHASE_ID_PERSONALIZE 0x1 #define PHASE_ID_AUTH1 0x2 #define PHASE_ID_AUTH2 0x3 #define PHASE_ID_SEQ_SERVICE 0x4 #define PHASE_ID_GET_PERSONALIZE_VERSION 0x5 #define PHASE_ID_GET_SYSTEM_INFO 0x6 #define PHASE_ID_FACTORY_INIT 0xFF //seq service ids #define SEQ_SERVICE_ID_GET_RTC 0x1 #define SEQ_SERVICE_ID_SET_CLOCK 0x2 #define SEQ_SERVICE_ID_GET_CLOCK 0x3 #define SEQ_SERVICE_ID_READ_DATA 0x4 #define SEQ_SERVICE_ID_WRITE_DATA 0x5 #define SEQ_SERVICE_ID_GET_RTC_STATUS 0x6 #define SEQ_SERVICE_ID_SET_RTC_STATUS 0x7 #define SEQ_SERVICE_ID_SET_RTC 0x8 #define SEQ_SERVICE_ID_CORRECT_RTC 0x9 #define SEQ_SERVICE_ID_SET_RTC2 0xA #define SEQ_SERVICE_ID_CORRECT_RTC2 0xB
Syscon packets[edit source]
Device Access Service (0x03)[edit source]
EEPROM write[edit source]
Purpose: Write block of data to EEPROM.
struct __attribute__ ((packed)) dev_access_request_t { uint8_t cmd; // 0x01 uint8_t padding[3]; uint32_t offset; uint32_t size; uint8_t data[0]; }; struct __attribute__ ((packed)) dev_access_response_t { uint8_t status; // 0x00:OK, 0x03:Wrong offset, 0x04:Wrong size, 0xFF:Error uint8_t data[0]; };
- Although this service is not used on Slim and some Phat consoles anymore, it is working (only on consoles with a CXR (Mullion) Syscon).
- It seems it have the same restrictions as NVS service.
EEPROM read[edit source]
Purpose: Read block of data from EEPROM.
struct __attribute__ ((packed)) dev_access_request_t { uint8_t cmd; // 0x00 uint8_t padding[3]; uint32_t offset; uint32_t size; }; struct __attribute__ ((packed)) dev_access_response_t { uint8_t status; // 0x00:OK, 0x03:Wrong offset, 0x04:Wrong size, 0xFF:Error uint8_t data[0]; };
- Although this service is not used on Slim and some Phat consoles anymore, it is working (only on consoles with a CXR (Mullion) Syscon).
- It seems it have the same restrictions as NVS service.
Thermal Service (0x11)[edit source]
Get temperature alert?[edit source]
Purpose: Used to get current temperature alert?.
struct __attribute__ ((packed)) get_thermal_alert_request_t { uint8_t cmd; // 0x20 uint8_t param; // 0xFF }; struct __attribute__ ((packed)) get_thermal_alert_response_t { uint8_t unk1; // status? uint8_t unk2; // status? uint8_t data[32]; };
Get temperature[edit source]
Purpose: Used to get current temperature.
struct __attribute__ ((packed)) get_temperature_request_t { uint8_t cmd; // 0x00 uint8_t tzone; // 0x00:CELL, 0x01:RSX }; struct __attribute__ ((packed)) get_temperature_response_t { uint8_t status; int8_t temperature_hi; int8_t temperature_lo; };
- Temperature in celsius: sprintf("%d.%d", temperature_hi, (temperature_lo * 100) / 256);
Configuration Service (0x12)[edit source]
Get XDR configuration[edit source]
Purpose: Used at bootloader during initialization.
struct __attribute__ ((packed)) get_xdr_config_request_t { uint8_t cmd; // 0x00 uint8_t param; // 0x00 }; struct __attribute__ ((packed)) get_xdr_config_response_t { uint8_t cmd; uint8_t padding[3]; uint8_t data[128]; };
struct __attribute__ ((packed)) get_xdr_config2_request_t { uint8_t cmd; // 0x01 uint8_t param; // 0x00 }; struct __attribute__ ((packed)) get_xdr_config2_response_t { uint8_t cmd; uint8_t data_size?; uint8_t padding[2]; uint8_t data[256]; };
- get_xdr_config2 returns more data (maybe different config?)
Get IDlog information[edit source]
Purpose: Can be used to get CID/eCID information.
struct __attribute__ ((packed)) get_idlog_info_request_t { uint8_t cmd; // 0x03 uint8_t param; // 0x01 }; struct __attribute__ ((packed)) get_idlog_info_response_t { uint8_t cmd; uint8_t padding[3]; uint8_t ecid[16]; uint8_t cid[6]; };
sc_config_info::be::get_reference_clock[edit source]
Purpose: Used to calculate an initial value of timebase register.
struct __attribute__ ((packed)) get_reference_clock_request_t { uint8_t cmd; // 0x03 uint8_t param; // 0x10 }; struct __attribute__ ((packed)) get_reference_clock_response_t { uint8_t cmd; uint8_t padding[3]; uint32_t ref_clock_value; uint32_t unk; };
sc_config_info::xdr::get_reference_clock[edit source]
struct __attribute__ ((packed)) get_reference_clock_request_t { uint8_t cmd; // 0x00 uint8_t param; // 0x10 }; struct __attribute__ ((packed)) get_reference_clock_response_t { uint8_t cmd; uint8_t padding[3]; uint32_t ref_clock_value; uint32_t unk; };
Get core clock multiplier[edit source]
Purpose: Used together with reference clock to calculate clock frequency.
struct __attribute__ ((packed)) get_core_clock_multiplier_request_t { uint8_t cmd; // 0x03 uint8_t param; // 0x00 }; struct __attribute__ ((packed)) get_core_clock_multiplier_response_t { uint8_t cmd; uint8_t padding[3]; uint8_t id; // 00:2, 01:4, 03: 8, 04:10, 05:12, 06:16, 07:20 };
- Clock frequency = core clock multiplier table[id] * reference clock.
- For example, if you have id=3 and reference clock=400000000, then your clock frequency will be: 400000000*8=3200000000 (3.2 Ghz)
Get platform ID[edit source]
Purpose: Used to get platform ID (Cytology, Cookie, etc).
struct __attribute__ ((packed)) get_platform_id_request_t { uint8_t cmd; // 0x20 uint8_t param; // 0x10 }; struct __attribute__ ((packed)) get_platform_id_response_t { uint8_t cmd; uint8_t padding[3]; uint8_t platform_id[8]; // ASCII-string };
Get hardware configuration[edit source]
Purpose: Used to get hardware configuration (PS2 emulation compatibility, card support, WLAN interface, etc).
struct __attribute__ ((packed)) get_hw_config_request_t { uint8_t cmd; // 0x22 }; struct __attribute__ ((packed)) get_hw_config_response_t { uint8_t cmd; uint8_t padding[3]; uint8_t data[12]; };
Get BE counters[edit source]
Purpose: Used to get several BE counters.
struct __attribute__ ((packed)) get_be_counters_request_t { uint8_t cmd; // 0x41 }; struct __attribute__ ((packed)) get_be_counters_response_t { uint8_t cmd; uint32_t total_operation_time; uint32_t power_on_counter; uint32_t power_off_counter; };
Power Service (0x13)[edit source]
Shutdown[edit source]
Purpose: Shutdown console.
struct __attribute__ ((packed)) shutdown_request_t { uint8_t cmd; // 0x00 };
Reboot[edit source]
Purpose: Reboot console.
struct __attribute__ ((packed)) reboot_request_t { uint8_t cmd; // 0x01 };
Query system power up cause[edit source]
Purpose: Get information about system power up cause.
struct __attribute__ ((packed)) query_system_power_up_cause_request_t { uint8_t cmd; // 0x10 }; struct __attribute__ ((packed)) query_system_power_up_cause_response_t { uint8_t cmd; uint8_t padding1[3]; uint32_t wake_source; uint8_t requested_os_context; uint8_t current_os_context; uint8_t requested_gr_context; uint8_t current_gr_context; uint8_t last_shutdown_cause; uint8_t padding2[3]; };
Get realtime clock[edit source]
Purpose: Get current value of realtime clock.
struct __attribute__ ((packed)) get_rtc_request_t { uint8_t cmd; // 0x33 }; struct __attribute__ ((packed)) get_rtc_response_t { uint8_t cmd; uint8_t padding[3]; uint32_t rtc; };
NVS Service (0x14)[edit source]
EEPROM write[edit source]
Purpose: Write block of data to EEPROM.
struct __attribute__ ((packed)) nvs_access_request_t { uint8_t cmd; // 0x10 uint8_t index; uint8_t offset; uint8_t size; // 0x00 for full block uint8_t data[0]; }; struct __attribute__ ((packed)) nvs_access_response_t { uint8_t status; // 0x00:OK, 0x02:Invalid block index, 0x03:Invalid offset/size uint8_t index; uint8_t offset; uint8_t size; uint8_t data[0]; };
EEPROM read[edit source]
Purpose: Read block of data from EEPROM.
struct __attribute__ ((packed)) nvs_access_request_t { uint8_t cmd; // 0x20 uint8_t index; uint8_t offset; uint8_t size; // 0x00 for full block }; struct __attribute__ ((packed)) nvs_access_response_t { uint8_t status; // 0x00:OK, 0x02:Invalid block index, 0x03:Invalid offset/size uint8_t index; uint8_t offset; uint8_t size; uint8_t data[0]; };
Version Service (0x18)[edit source]
Get service version[edit source]
Purpose: Get information about service version.
struct __attribute__ ((packed)) get_service_version_request_t { uint8_t cmd; // 0x01 uint8_t service_id; }; struct __attribute__ ((packed)) get_service_version_response_t { uint8_t status; uint8_t service_id; uint8_t major_version; uint8_t minor_version; };
Get syscon version[edit source]
Purpose: Get information about syscon version.
Note: Syscon Auth required.
struct __attribute__ ((packed)) get_sc_version_request_t { uint8_t cmd; // 0x12/0x14 }; struct __attribute__ ((packed)) get_sc_version_response_t { uint8_t cmd; uint8_t padding[3]; uint16_t version; };
Livelock Service (0x1B)[edit source]
Set livelock detection mode[edit source]
struct __attribute__ ((packed)) set_livelock_detection_mode_request_t { uint8_t cmd; // 0x10 uint8_t param; //0x00 }; struct __attribute__ ((packed)) set_livelock_detection_mode_response_t { uint8_t data; // };
A/V Service (0x30)[edit source]
Get monitor information[edit source]
Purpose: Get monitor information (EDID).
struct __attribute__ ((packed)) get_monitor_info_request_t { uint8_t cmd; // 0x0D uint8_t av_port; // 0x00 uint8_t unk1; // 0x00 uint8_t unk2; // 0x00 (garbage) }; struct __attribute__ ((packed)) get_monitor_info_response_t { uint8_t status; uint8_t unk1; uint8_t unk2; uint8_t unk3; uint8_t data[256]; };
Sample responses when HDMI inserted (personal info removed):
000c000: 3001 0000 0000 8031 8000 0000 0104 0104 0......1........ 000c010: 2000 0282 00ff ffff ffff ff00 0469 fa22 ............i." 000c020: 0101 0101 2c15 0103 8030 1b78 eec4 f5a3 ....,....0.x.... 000c030: 574a 9c23 1150 54bf ef00 714f 8180 8140 WJ.#.PT...qO...@ 000c040: 9500 a940 b300 d1c0 0101 023a 8018 7138 ...@.......:..q8 000c050: 2d40 582c 4500 132b 2100 001e 0000 00fd -@X,E..+!....... 000c060: 0032 4c1e 5311 000a 2020 2020 2020 0000 .2L.S... .. 000c070: 00fc 0056 4532 3238 0a20 2020 2020 2020 ...VE228. 000c080: 0000 00ff 00XX XXXX XXXX XXXX XXXX XXXX .....EDIDSERIALN 000c090: XX0a 0188 0203 1ef1 4b90 0504 0302 0111 M.......K....... 000c0a0: 1213 141f 2309 0707 8301 0000 6503 0c00 ....#.......e... 000c0b0: 1000 1a36 80a0 7038 1e40 3020 3500 132b ...6..p8.@0 5..+ 000c0c0: 2100 001a 6621 56aa 5100 1e30 468f 3300 !...f!V.Q..0F.3. 000c0d0: 132b 2100 001e 011d 0072 51d0 1e20 6e28 .+!......rQ.. n( 000c0e0: 5500 132b 2100 001e 8c0a d08a 20e0 2d10 U..+!....... .-. 000c0f0: 103e 9600 132b 2100 0018 011d 8018 711c .>...+!.......q. 000c100: 1620 582c 2500 132b 2100 009f 0000 0000 . X,%..+!.......
0000h: 00 FF FF FF FF FF FF 00 XX XX XX XX XX XX XX XX .яяяяяя......... 0010h: 26 17 01 03 80 3C 22 78 2E 2F A5 A5 54 50 9E 27 &...Ђ<"x./ҐҐTPћ' 0020h: 10 50 54 A5 6B 80 D1 C0 61 C0 81 00 81 C0 81 40 .PTҐkЂСАaАЃ.ЃАЃ@ 0030h: 81 80 A9 C0 B3 00 02 3A 80 18 71 38 2D 40 58 2C ЃЂ©Аі..:Ђ.q8-@X, 0040h: 45 00 56 50 21 00 00 1E 00 00 00 FF 00 41 39 44 E.VP!......я.A9D 0050h: 30 30 36 31 30 53 4C 30 0A 20 00 00 00 FD 00 32 00610SL0. ...э.2 0060h: 4C 1E 53 15 00 0A 20 20 20 20 20 20 00 00 00 FC L.S... ...ь 0070h: 00 XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX ................ 0080h: 02 03 24 F1 4F 01 02 03 04 05 06 07 10 11 12 13 ..$сO........... 0090h: 14 15 16 1F 23 09 07 07 83 01 00 00 67 03 0C 00 ....#...ѓ...g... 00A0h: 10 00 00 2D 02 3A 80 18 71 38 2D 40 58 2C 45 00 ...-.:Ђ.q8-@X,E. 00B0h: 56 50 21 00 00 1F 01 1D 80 18 71 1C 16 20 58 2C VP!.....Ђ.q.. X, 00C0h: 25 00 56 50 21 00 00 9F 01 1D 00 72 51 D0 1E 20 %.VP!..џ...rQР. 00D0h: 6E 28 55 00 56 50 21 00 00 1E 8C 0A D0 8A 20 E0 n(U.VP!...Њ.РЉ а 00E0h: 2D 10 10 3E 96 00 56 50 21 00 00 18 00 00 00 00 -..>–.VP!....... 00F0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 ...............–
Get HDMI KSV[edit source]
Purpose: Get HDMI key selection vector.
struct __attribute__ ((packed)) get_hdmi_ksv_request_t { uint8_t cmd; // 0x11 }; struct __attribute__ ((packed)) get_hdmi_ksv_response_t { uint8_t result; uint8_t padding[3]; uint8_t ksv[5]; };
Tools[edit source]
- SysCommParser by TizzyT http://tizzyt-archive.blogspot.com.es/2015/02/syscommparser.html
Log Info[edit source]
Each of the times a request is made that involves Auth1+Auth2,there is a communication made to eeprom for read/write. It goes like this (case for backup_srh):
- Read initial 16 bytes of eeprom once (packet 0) (0x10)
- Read 32 bytes following 20 times (packets 1-21) (0x280)
- Auth1 established
- Repeat steps 1 2
- Auth2 established
- Repeat steps 1 2 for finding key?/validation?
- Validation/Finding Key established
- Final Packet(s): Read EEPROM Location/Write to EEPROM Location/Do Other Tasks
Notes:
- Initial bytes are constant and never change from phat to phat
- Following bytes are perconsole (EID1 most likely)
- Steps that don't involve sc auth1 auth2 such as reading eeprom using nvs service or checking syscon status (probably stored in RAM, because true value is in EEPROM encrypted) have no packet activity
- initial bytes value: 99 D9 66 2B B3 D7 61 54 6B 9C 3F 9E D1 40 ED B0
- we haven't tested bootldr init yet, but we think it might be different from the others...
Credits to ZeroTolerance for the info
Log[edit source]
http://pastie.org/private/0pnjuk1hp3qybqdjtpryq
- KickStart Value (packet 0): 1.8480055 seconds
- KickEnd Value : (packet 69): 5.8694735 seconds
- Diff : 4.021468 seconds
LiveLock and Versioning Confusion[edit source]
See :
- https://www.psdevwiki.com/ps3/File:C1kVeMD.png
- https://www.psdevwiki.com/ps3/File:NBkrqqZ.png
- database : https://www.sendspace.com/file/6awfi9
For proof