Function bodies 388 total
derive_iv function · python · L68-L85 (18 LOC)research/tools/ps3_register.py
def derive_iv(platform, context_bytes=None):
"""Derive IV with optional context XOR"""
if context_bytes is None:
context_bytes = bytes(8)
if platform == "Phone":
iv = bytearray(REG_IV_PHONE)
for i in range(8):
iv[8 + i] ^= context_bytes[i] # Phone: XOR second 8 bytes
elif platform == "PSP":
iv = bytearray(REG_IV_PSP)
for i in range(8):
iv[i] ^= context_bytes[i]
else: # PC
iv = bytearray(REG_IV_PC)
for i in range(8):
iv[i] ^= context_bytes[i]
return bytes(iv)try_register function · python · L87-L220 (134 LOC)research/tools/ps3_register.py
def try_register(ps3_ip, pin, platform, device_id, device_mac, device_name, context_bytes):
"""Attempt registration with given parameters"""
platform_names = {"Phone": "Phone", "PSP": "PSP", "PC": "PC"}
pname = platform_names.get(platform, platform)
log("REG", f"--- Trying platform={pname}, context={context_bytes.hex()} ---")
# Build plaintext body
body_text = (
f"Client-Type: {pname}\r\n"
f"Client-Id: {device_id.hex()}\r\n"
f"Client-Mac: {device_mac.hex()}\r\n"
f"Client-Nickname: {device_name}\r\n"
)
log("REG", f"Body: {body_text.strip()}")
# Generate 16 random bytes as key material
key_material = os.urandom(16)
log("REG", f"Key material: {key_material.hex()}")
# Derive AES key
if platform == "Phone":
aes_key = derive_key_phone(key_material)
elif platform == "PSP":
aes_key = derive_key_psp(key_material)
else:
aes_key = derive_key_pc(key_material)
log("REG", f"AESmain function · python · L223-L304 (82 LOC)research/tools/ps3_register.py
def main():
if len(sys.argv) < 3:
print("Usage: python3 ps3_register.py <PS3_IP> <8-digit-PIN>")
print("Example: python3 ps3_register.py 192.168.1.75 78831915")
sys.exit(1)
ps3_ip = sys.argv[1]
pin = sys.argv[2]
log("MAIN", f"PS3 IP: {ps3_ip}")
log("MAIN", f"PIN: {pin}")
# Generate a device identity
device_id = os.urandom(16)
device_mac = os.urandom(6)
device_name = "PsOldRemotePlay"
log("MAIN", f"Device ID: {device_id.hex()}")
log("MAIN", f"Device MAC: {device_mac.hex()}")
log("MAIN", "")
# Context byte candidates
pin_bytes = pin.encode("ascii")[:8].ljust(8, b'\x00')
pin_int = int(pin) if pin.isdigit() else 0
pin_be = pin_int.to_bytes(8, "big")
context_candidates = [
(bytes(8), "zeros"),
(pin_bytes, "PIN as ASCII"),
(pin_be, "PIN as big-endian int"),
(bytes.fromhex(pin.ljust(16, '0'))[:8], "PIN as hex bytes"),
]
# Try each platform with each contexderive_key_phone function · python · L21-L25 (5 LOC)research/tools/ps3_register_single.py
def derive_key_phone(km):
k = bytearray(16)
for i in range(16):
k[i] = ((km[i] - i - 0x28) ^ REG_XOR_PHONE[i]) & 0xFF
return bytes(k)derive_iv_phone function · python · L27-L31 (5 LOC)research/tools/ps3_register_single.py
def derive_iv_phone(ctx8):
iv = bytearray(REG_IV_PHONE)
for i in range(8):
iv[8+i] ^= ctx8[i]
return bytes(iv)attempt function · python · L33-L114 (82 LOC)research/tools/ps3_register_single.py
def attempt(ps3_ip, pin, ctx8, ctx_name, client_type_str="Phone"):
print(f"\n>>> Client-Type={client_type_str}, Context={ctx_name} ({ctx8.hex()})")
body = (f"Client-Type: {client_type_str}\r\n"
f"Client-Id: {DEVICE_ID.hex()}\r\n"
f"Client-Mac: {DEVICE_MAC.hex()}\r\n"
f"Client-Nickname: PsOldRemotePlay\r\n")
km = os.urandom(16)
aes_key = derive_key_phone(km)
aes_iv = derive_iv_phone(ctx8)
plain = body.encode("ascii")
padded = plain.ljust(((len(plain)+15)//16)*16, b'\x00')
encrypted = AES.new(aes_key, AES.MODE_CBC, aes_iv).encrypt(padded)
full_body = encrypted + km
print(f" AES Key: {aes_key.hex()}")
print(f" AES IV: {aes_iv.hex()}")
print(f" KeyMat: {km.hex()}")
print(f" Body size: {len(full_body)}")
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
sock.connect((ps3_ip, 9293))
req = f"POST /sce/premo/regist HTTPmain function · python · L116-L177 (62 LOC)research/tools/ps3_register_single.py
def main():
if len(sys.argv) < 3:
print("PS3 Registration — Single Shot v3")
print("Usage: python3 ps3_register_single.py <IP> <PIN> [attempt]")
print("")
print("Attempts:")
print(" 1 = Phone, Client-Type:Phone, ctx=zeros")
print(" 2 = Phone, Client-Type:Phone, ctx=PIN-ascii")
print(" 3 = Phone, Client-Type:Phone, ctx=PIN-be-int")
print(" 4 = Phone, Client-Type:PC, ctx=zeros (shifted mapping)")
print(" 5 = Phone, Client-Type:PC, ctx=PIN-ascii (shifted mapping)")
print(" 6 = Phone, Client-Type:PC, ctx=PIN-be-int (shifted mapping)")
print(" 7 = Phone, Client-Type:Phone, ctx=PIN-le-int")
print(" 8 = Phone, Client-Type:Phone, ctx=SSID-suffix-padded")
print(" 9 = Phone, Client-Type:Phone, ctx=PIN-first4-repeated")
print(" 0 = Try ALL (3 per PS3 session)")
sys.exit(1)
ip = sys.argv[1]
pin = sys.argv[2]
n = int(sys.argv[3]) if len(sys.argv)Repobility · code-quality intelligence platform · https://repobility.com
derive_key function · python · L71-L96 (26 LOC)research/tools/ps3_register_v2.py
def derive_key(material: bytes, platform_type: int) -> bytes:
"""Derive the AES key from 16 bytes of key material using the platform-specific formula."""
params = CRYPTO_KEYS[platform_type]
xor_key = params["xor_key"]
const = params["constant"]
formula = params["formula"]
result = bytearray(16)
for i in range(16):
mat = material[i]
key = xor_key[i]
if formula == "sub_first":
# Phone (type 2): subtract first, then XOR
# result = (material - counter - constant) XOR key
val = (mat - i - const) & 0xFF
val = val ^ key
else: # xor_first
# PSP (type 1) and PC (type 3): XOR first, then subtract
# result = (material XOR key) - counter - constant
val = mat ^ key
val = (val - i - const) & 0xFF
result[i] = val
return bytes(result)get_iv function · python · L99-L101 (3 LOC)research/tools/ps3_register_v2.py
def get_iv(platform_type: int) -> bytes:
"""Get the IV base for the given platform type."""
return CRYPTO_KEYS[platform_type]["iv_base"]build_registration_body function · python · L104-L125 (22 LOC)research/tools/ps3_register_v2.py
def build_registration_body(device_id: bytes, device_mac: bytes, device_name: str,
client_type: str, key_material: bytes = None) -> tuple:
"""Build the plaintext body for registration, returns (body, key_material)."""
# For Phone type, the client generates 16 random bytes as key material
if key_material is None:
key_material = os.urandom(16)
# Build plaintext body
body_lines = []
body_lines.append(f"Client-Type: {client_type}")
body_lines.append(f"Client-Id: {device_id.hex()}")
body_lines.append(f"Client-Mac: {device_mac.hex()}")
body_lines.append(f"Client-Nickname: {device_name}")
plaintext = "\r\n".join(body_lines) + "\r\n"
# Pad to AES block size (16 bytes)
pt_bytes = plaintext.encode("ascii")
pad_len = 16 - (len(pt_bytes) % 16)
pt_bytes += bytes([pad_len] * pad_len) # PKCS7 padding
return pt_bytes, key_materialattempt_registration function · python · L128-L222 (95 LOC)research/tools/ps3_register_v2.py
def attempt_registration(ps3_ip: str, pin: str, platform_type: int):
"""Attempt a single registration with the given platform type."""
params = CRYPTO_KEYS[platform_type]
print(f"\n{'='*60}")
print(f"REGISTRATION ATTEMPT — Platform: {params['name']} (type {platform_type})")
print(f" Formula: {params['formula']}, constant: 0x{params['constant']:02X}")
print(f" XOR key: {params['xor_key'].hex()}")
print(f" IV base: {params['iv_base'].hex()}")
print(f"{'='*60}")
# WiFi password = PIN halves swapped
wifi_pw = pin[4:] + pin[:4]
print(f" PIN: {pin}, WiFi password: {wifi_pw}")
# Generate key material (16 random bytes for Phone type)
key_material = os.urandom(16)
print(f" Key material: {key_material.hex()}")
# Derive AES key
aes_key = derive_key(key_material, platform_type)
print(f" Derived AES key: {aes_key.hex()}")
# Get IV
iv = get_iv(platform_type)
print(f" AES IV: {iv.hex()}")
# Device info
main function · python · L225-L263 (39 LOC)research/tools/ps3_register_v2.py
def main():
print("PS3 Remote Play Registration v2")
print(f"Target: {PS3_IP}:{PORT}")
print(f"PIN: {PIN}")
print(f"Requested platform: {PLATFORM}")
print()
# Check connectivity
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(3)
sock.connect((PS3_IP, PORT))
sock.close()
print(f"[+] Port {PORT} is OPEN")
except:
print(f"[!] Cannot connect to {PS3_IP}:{PORT}")
print(" Make sure PS3 is in registration mode (showing PIN)")
return
# Try the requested platform type
if attempt_registration(PS3_IP, PIN, PLATFORM):
return
# If that failed, try all platform types
print(f"\n\nPlatform {PLATFORM} failed. Trying all types...")
for pt in [1, 2, 3]:
if pt == PLATFORM:
continue
if attempt_registration(PS3_IP, PIN, pt):
return
print("\n\n[!] All platform types failed.")
print(" The key derivatiderive_key_phone function · python · L25-L29 (5 LOC)research/tools/ps3_register_v4.py
def derive_key_phone(km):
k = bytearray(16)
for i in range(16):
k[i] = ((km[i] - i - 0x28) ^ REG_XOR_PHONE[i]) & 0xFF
return bytes(k)derive_iv_phone function · python · L31-L35 (5 LOC)research/tools/ps3_register_v4.py
def derive_iv_phone(ctx8):
iv = bytearray(REG_IV_PHONE)
for i in range(8):
iv[8+i] ^= ctx8[i]
return bytes(iv)send_registration function · python · L37-L89 (53 LOC)research/tools/ps3_register_v4.py
def send_registration(ps3_ip, body_bytes):
"""Send raw registration POST and return response"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
sock.connect((ps3_ip, 9293))
req = f"POST /sce/premo/regist HTTP/1.1\r\nContent-Length: {len(body_bytes)}\r\n\r\n"
sock.sendall(req.encode("ascii") + body_bytes)
print(" Sent! Waiting...")
resp = b""
while True:
try:
c = sock.recv(4096)
if not c: break
resp += c
if b"\r\n\r\n" in resp:
he = resp.index(b"\r\n\r\n") + 4
hdr = resp[:he].decode("ascii", errors="replace")
cl = 0
for l in hdr.split("\r\n"):
if l.lower().startswith("content-length:"): cl = int(l.split(":")[1].strip())
if len(resp) - he >= cl: break
except: break
Repobility analyzer · published findings · https://repobility.com
try_encrypted function · python · L91-L110 (20 LOC)research/tools/ps3_register_v4.py
def try_encrypted(ps3_ip, ctx8, ctx_name, client_type="Phone", line_ending="\r\n"):
print(f"\n>>> Encrypted: Client-Type={client_type}, Context={ctx_name} ({ctx8.hex()}), endings={'CRLF' if line_ending == chr(13)+chr(10) else 'LF'}")
body = (f"Client-Type: {client_type}{line_ending}"
f"Client-Id: {DEVICE_ID.hex()}{line_ending}"
f"Client-Mac: {DEVICE_MAC.hex()}{line_ending}"
f"Client-Nickname: PsOldRemotePlay{line_ending}")
km = os.urandom(16)
aes_key = derive_key_phone(km)
aes_iv = derive_iv_phone(ctx8)
plain = body.encode("ascii")
padded = plain.ljust(((len(plain)+15)//16)*16, b'\x00')
encrypted = AES.new(aes_key, AES.MODE_CBC, aes_iv).encrypt(padded)
full_body = encrypted + km
print(f" Key:{aes_key.hex()} IV:{aes_iv.hex()}")
return send_registration(ps3_ip, full_body)try_unencrypted function · python · L112-L121 (10 LOC)research/tools/ps3_register_v4.py
def try_unencrypted(ps3_ip, client_type="Phone"):
print(f"\n>>> UNENCRYPTED: Client-Type={client_type}")
body = (f"Client-Type: {client_type}\r\n"
f"Client-Id: {DEVICE_ID.hex()}\r\n"
f"Client-Mac: {DEVICE_MAC.hex()}\r\n"
f"Client-Nickname: PsOldRemotePlay\r\n")
print(f" Raw body: {len(body)} bytes")
return send_registration(ps3_ip, body.encode("ascii"))main function · python · L123-L160 (38 LOC)research/tools/ps3_register_v4.py
def main():
if len(sys.argv) < 3:
print("Usage: python3 ps3_register_v4.py <IP> <PIN> [attempt 1-9]")
print(" 1 = Phone, ctx=PS3 ethernet MAC+00+00")
print(" 2 = Phone, ctx=PS3 MAC reversed")
print(" 3 = Phone, ctx=PS3 MAC+01+00 (WiFi MAC = eth+1?)")
print(" 4 = UNENCRYPTED body (Phone)")
print(" 5 = UNENCRYPTED body (PC)")
print(" 6 = Phone, ctx=zeros, LF line endings")
print(" 7 = Phone, ctx=PS3 MAC first 4 + PIN first 4")
print(" 8 = Phone, ctx=all 0xFF")
print(" 9 = Phone, ctx=PS3 MAC XOR PIN bytes")
sys.exit(1)
ip = sys.argv[1]
pin = sys.argv[2]
n = int(sys.argv[3]) if len(sys.argv) > 3 else 1
# PS3 ethernet MAC: FC:0F:E6:D5:67:95
mac8_padded = PS3_ETH_MAC + bytes(2) # fc0fe6d5679500 00
mac8_reversed = bytes(reversed(PS3_ETH_MAC)) + bytes(2) # 9567d5e60ffc00 00
mac8_wifi = bytes([PS3_ETH_MAC[0], PS3_ETH_MAC[1], PSderive_key function · python · L26-L29 (4 LOC)research/tools/ps3_register_v5.py
def derive_key(km):
k = bytearray(16)
for i in range(16): k[i] = ((km[i] - i - 0x28) ^ REG_XOR_PHONE[i]) & 0xFF
return bytes(k)derive_iv function · python · L31-L34 (4 LOC)research/tools/ps3_register_v5.py
def derive_iv(ctx8):
iv = bytearray(REG_IV_PHONE)
for i in range(8): iv[8+i] ^= ctx8[i]
return bytes(iv)attempt function · python · L36-L100 (65 LOC)research/tools/ps3_register_v5.py
def attempt(ps3_ip, ctx8, label, ctype="Phone"):
print(f"\n>>> {label}: ctx={ctx8.hex()}, Client-Type={ctype}")
body = (f"Client-Type: {ctype}\r\n"
f"Client-Id: {DEVICE_ID.hex()}\r\n"
f"Client-Mac: {DEVICE_MAC.hex()}\r\n"
f"Client-Nickname: PsOldRemotePlay\r\n")
km = os.urandom(16)
key = derive_key(km)
iv = derive_iv(ctx8)
plain = body.encode("ascii")
padded = plain.ljust(((len(plain)+15)//16)*16, b'\x00')
enc = AES.new(key, AES.MODE_CBC, iv).encrypt(padded)
print(f" Key:{key.hex()} IV:{iv.hex()}")
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(10)
s.connect((ps3_ip, 9293))
req = f"POST /sce/premo/regist HTTP/1.1\r\nContent-Length: {len(enc)+16}\r\n\r\n"
s.sendall(req.encode() + enc + km)
print(" Sent!")
resp = b""
while True:
try:
c = s.recv(4096)
if not c: break
main function · python · L102-L141 (40 LOC)research/tools/ps3_register_v5.py
def main():
if len(sys.argv) < 3:
print("Usage: python3 ps3_register_v5.py <IP> <PIN> [1-9]")
sys.exit(1)
ip = sys.argv[1]
pin = sys.argv[2]
n = int(sys.argv[3]) if len(sys.argv)>3 else 0
# sockaddr_in: family(2) + port(2) + addr(4) = 8 bytes
# PS3 WiFi AP typically uses 192.168.1.1
sa_wifi = bytes([0x00,0x02, 0x24,0x4D, 0xC0,0xA8,0x01,0x01])
ip_wifi = bytes([0xC0,0xA8,0x01,0x01, 0x00,0x00,0x00,0x00])
wifi_mac = bytes([0xFC,0x0F,0xE6,0xD5,0x67,0x96,0x00,0x00]) # eth+1
eth_mac = bytes([0xFC,0x0F,0xE6,0xD5,0x67,0x95,0x00,0x00])
sa_wired = bytes([0x00,0x02, 0x24,0x4D, 0xC0,0xA8,0x01,0x4B]) # 192.168.1.75
sa_be_port = bytes([0x00,0x02, 0x4D,0x24, 0xC0,0xA8,0x01,0x01]) # swapped port bytes
sa_noport = bytes([0x00,0x02, 0x00,0x00, 0xC0,0xA8,0x01,0x01])
attempts = [
(sa_wifi, "sockaddr(WiFi 192.168.1.1:9293)", "Phone"),
(ip_wifi, "IP 192.168.1.1 padded", "Phone"),
(wifi_mac, detect_format function · python · L77-L114 (38 LOC)research/tools/repo_analysis/binary_analyzer.py
def detect_format(data: bytes) -> dict:
"""Detect binary format from headers."""
info = {'format': 'unknown', 'bits': 0, 'arch': 'unknown'}
if len(data) < 4:
return info
# PE (MZ header)
if data[:2] == b'MZ':
info['format'] = 'PE'
if len(data) > 0x3C + 4:
pe_offset = struct.unpack_from('<I', data, 0x3C)[0]
if pe_offset < len(data) - 6 and data[pe_offset:pe_offset+4] == b'PE\x00\x00':
machine = struct.unpack_from('<H', data, pe_offset + 4)[0]
info['arch'] = {0x14c: 'x86', 0x8664: 'x64', 0x1c0: 'ARM',
0xaa64: 'ARM64'}.get(machine, f'0x{machine:04x}')
magic_offset = pe_offset + 24
if magic_offset < len(data) - 2:
opt_magic = struct.unpack_from('<H', data, magic_offset)[0]
info['bits'] = 64 if opt_magic == 0x20b else 32
# ELF
elif data[:4] == b'\x7fELF':
info['format'] =Repobility (the analyzer behind this table) · https://repobility.com
extract_strings function · python · L117-L165 (49 LOC)research/tools/repo_analysis/binary_analyzer.py
def extract_strings(data: bytes, min_len: int = MIN_STRING_LEN) -> list:
"""Extract ASCII and wide (UTF-16LE) strings."""
strings = []
# ASCII strings
current = bytearray()
start = 0
for i, b in enumerate(data):
if 32 <= b < 127:
if not current:
start = i
current.append(b)
else:
if len(current) >= min_len:
strings.append({
'offset': start,
'value': current.decode('ascii'),
'encoding': 'ascii',
'length': len(current)
})
current = bytearray()
if len(current) >= min_len:
strings.append({
'offset': start,
'value': current.decode('ascii'),
'encoding': 'ascii',
'length': len(current)
})
# Wide strings (UTF-16LE): printable char followed by 0x00
current = bytearray()
start = 0
for i in range(0, lfind_pattern function · python · L168-L186 (19 LOC)research/tools/repo_analysis/binary_analyzer.py
def find_pattern(data: bytes, pattern: bytes) -> list:
"""Find all occurrences of a byte pattern in data."""
results = []
start = 0
while True:
pos = data.find(pattern, start)
if pos == -1:
break
# Get context: 16 bytes before and after
ctx_start = max(0, pos - 16)
ctx_end = min(len(data), pos + len(pattern) + 16)
context_hex = data[ctx_start:ctx_end].hex()
results.append({
'offset': pos,
'offset_hex': f'0x{pos:x}',
'context': context_hex,
})
start = pos + 1
return resultscount_prologues function · python · L189-L203 (15 LOC)research/tools/repo_analysis/binary_analyzer.py
def count_prologues(data: bytes) -> dict:
"""Count function prologues to estimate architecture usage."""
counts = {}
for name, pattern in PROLOGUES.items():
count = 0
start = 0
while True:
pos = data.find(pattern, start)
if pos == -1:
break
count += 1
start = pos + 1
if count > 0:
counts[name] = count
return countsanalyze_file function · python · L206-L269 (64 LOC)research/tools/repo_analysis/binary_analyzer.py
def analyze_file(filepath: str, quiet: bool = False, strings_only: bool = False) -> dict:
"""Analyze a single binary file."""
result = {
'file': filepath,
'size': os.path.getsize(filepath),
}
try:
with open(filepath, 'rb') as f:
data = f.read()
except (OSError, PermissionError) as e:
result['error'] = str(e)
return result
if not quiet:
print(f" Analyzing: {filepath} ({len(data)} bytes)")
# Format detection
result['format'] = detect_format(data)
# Strings
if not quiet:
print(f" Extracting strings...")
strings = extract_strings(data)
result['strings_count'] = len(strings)
# Filter interesting strings
interesting_keywords = ['aes', 'cbc', 'encrypt', 'decrypt', 'key', 'iv', 'nonce',
'regist', 'premo', 'remote', 'play', 'session', 'auth',
'pin', 'cert', 'ssl', 'tls', 'http', 'sce/', 'sie/',
main function · python · L272-L351 (80 LOC)research/tools/repo_analysis/binary_analyzer.py
def main():
parser = argparse.ArgumentParser(
description='Analyze binary files for crypto patterns and structure')
parser.add_argument('target', help='File or directory to analyze')
parser.add_argument('-o', '--output', help='Output JSON file path')
parser.add_argument('-q', '--quiet', action='store_true', help='Suppress progress')
parser.add_argument('--strings-only', action='store_true', help='Only extract strings')
args = parser.parse_args()
target = args.target
if not os.path.exists(target):
print(f"ERROR: Path not found: {target}", file=sys.stderr)
sys.exit(1)
# Collect files
files = []
if os.path.isfile(target):
files.append(target)
else:
for root, dirs, filenames in os.walk(target):
dirs[:] = [d for d in dirs if not d.startswith('.')]
for fname in filenames:
ext = os.path.splitext(fname)[1].lower()
if ext in BINARY_EXTENSIONS:
ps3_derive_key function · python · L70-L88 (19 LOC)research/tools/repo_analysis/compare_key_derivation.py
def ps3_derive_key(device_type: str, km: bytes) -> bytes:
"""Derive AES key from key material for a PS3 device type."""
info = PS3_KEYS[device_type]
xor_key = info['xor_key']
base = info['key_subtract_base']
key = bytearray(16)
for i in range(16):
if device_type == 'PSP':
# key[i] = (km[i] ^ XOR[i]) - i - 0x25
key[i] = ((km[i] ^ xor_key[i]) - i - 0x25) & 0xFF
elif device_type == 'Phone':
# key[i] = (km[i] - i - 0x28) ^ XOR[i]
key[i] = ((km[i] - i - 0x28) & 0xFF) ^ xor_key[i]
elif device_type == 'PC':
# key[i] = (km[i] + i + 0x29) ^ XOR[i]
key[i] = ((km[i] + i + 0x29) & 0xFF) ^ xor_key[i]
return bytes(key)ps3_derive_iv function · python · L91-L108 (18 LOC)research/tools/repo_analysis/compare_key_derivation.py
def ps3_derive_iv(device_type: str, ctx8: bytes) -> bytes:
"""Derive AES IV from IV base and 8-byte context value."""
iv = bytearray(PS3_KEYS[device_type]['iv_base'])
if device_type == 'PSP':
# XOR first 8 bytes
for i in range(8):
iv[i] ^= ctx8[i]
elif device_type == 'Phone':
# XOR last 8 bytes
for i in range(8):
iv[8 + i] ^= ctx8[i]
elif device_type == 'PC':
# XOR first 8 bytes
for i in range(8):
iv[i] ^= ctx8[i]
return bytes(iv)ps4_derive_handshake_key function · python · L115-L121 (7 LOC)research/tools/repo_analysis/compare_key_derivation.py
def ps4_derive_handshake_key(nonce: bytes, morning: bytes) -> bytes:
"""
PS4 (chiaki-ng): HMAC-SHA256 based key derivation.
key = HMAC-SHA256(morning, nonce)[:16]
"""
h = hmac.new(morning, nonce, hashlib.sha256).digest()
return h[:16]Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
ps4_derive_regist_key function · python · L124-L133 (10 LOC)research/tools/repo_analysis/compare_key_derivation.py
def ps4_derive_regist_key(pin: str, rpkey: bytes) -> bytes:
"""
PS4 registration key derivation (chiaki pattern):
Uses PIN + static key to derive AES key via HMAC/SHA.
This is a simplified model based on chiaki-ng source.
"""
pin_bytes = pin.encode('ascii')
# chiaki uses: key = HMAC-SHA256(rpkey, pin_bytes)[:16]
h = hmac.new(rpkey, pin_bytes, hashlib.sha256).digest()
return h[:16]generate_iv_contexts function · python · L140-L205 (66 LOC)research/tools/repo_analysis/compare_key_derivation.py
def generate_iv_contexts(pin_str: str) -> list:
"""Generate all plausible 8-byte IV context values from PIN."""
pin_int = int(pin_str)
contexts = []
# 1. All zeros (baseline)
contexts.append(('zeros', bytes(8)))
# 2. PIN as big-endian int64
contexts.append(('PIN BE i64', pin_int.to_bytes(8, 'big')))
# 3. PIN as little-endian int64
contexts.append(('PIN LE i64', pin_int.to_bytes(8, 'little')))
# 4. PIN as BE i32 left-padded
contexts.append(('PIN BE i32 left', b'\x00\x00\x00\x00' + pin_int.to_bytes(4, 'big')))
# 5. PIN as BE i32 right-padded
contexts.append(('PIN BE i32 right', pin_int.to_bytes(4, 'big') + b'\x00\x00\x00\x00'))
# 6. PIN as LE i32 left-padded
contexts.append(('PIN LE i32 left', b'\x00\x00\x00\x00' + pin_int.to_bytes(4, 'little')))
# 7. PIN as LE i32 right-padded
contexts.append(('PIN LE i32 right', pin_int.to_bytes(4, 'little') + b'\x00\x00\x00\x00'))
# 8. PIN ASCII bytes zero-padded to 8
hex_str function · python · L208-L210 (3 LOC)research/tools/repo_analysis/compare_key_derivation.py
def hex_str(data: bytes) -> str:
"""Format bytes as hex string."""
return ' '.join(f'{b:02X}' for b in data)main function · python · L213-L310 (98 LOC)research/tools/repo_analysis/compare_key_derivation.py
def main():
pin = sys.argv[1] if len(sys.argv) > 1 else '12345678'
if pin in ('-h', '--help'):
print(__doc__.strip())
sys.exit(0)
print(f"{'='*70}")
print(f"PS3/PS4 Registration Key Derivation Comparison")
print(f"{'='*70}")
print(f"PIN: {pin}")
print()
# Sample key material (would come from PS3 in real handshake)
km = bytes(range(16)) # Placeholder
print(f"Sample KM (key material): {hex_str(km)}")
print()
# ============================================================
# PS3 Key Derivation — All 3 device types
# ============================================================
print(f"{'='*70}")
print(f"PS3 KEY DERIVATION")
print(f"{'='*70}")
for dtype in ['PSP', 'Phone', 'PC']:
info = PS3_KEYS[dtype]
key = ps3_derive_key(dtype, km)
print(f"\n --- {dtype} ---")
print(f" Formula: {info['key_formula']}")
print(f" XOR key: {hex_str(info['xor_key'])}")
Match class · python · L73-L79 (7 LOC)research/tools/repo_analysis/extract_aes_patterns.py
class Match:
file: str
line_num: int
pattern_type: str
matched_text: str
context_before: list = field(default_factory=list)
context_after: list = field(default_factory=list)scan_file function · python · L82-L112 (31 LOC)research/tools/repo_analysis/extract_aes_patterns.py
def scan_file(filepath: Path) -> list:
"""Scan a single source file for crypto patterns."""
matches = []
try:
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
lines = f.readlines()
except (OSError, PermissionError):
return matches
for line_idx, line in enumerate(lines):
for pattern_name, pattern in CRYPTO_PATTERNS.items():
for m in pattern.finditer(line):
ctx_before = [
lines[i].rstrip()
for i in range(max(0, line_idx - CONTEXT_LINES), line_idx)
]
ctx_after = [
lines[i].rstrip()
for i in range(line_idx + 1, min(len(lines), line_idx + CONTEXT_LINES + 1))
]
matches.append(Match(
file=str(filepath),
line_num=line_idx + 1,
pattern_type=pattern_name,
matched_text=m.grouprint_match function · python · L115-L136 (22 LOC)research/tools/repo_analysis/extract_aes_patterns.py
def print_match(match: Match, repos_dir: str):
"""Pretty-print a single match."""
rel_path = os.path.relpath(match.file, repos_dir)
print(f"\n \033[1;33m[{match.pattern_type}]\033[0m {rel_path}:{match.line_num}")
print(f" Matched: \033[1;32m{match.matched_text}\033[0m")
if match.context_before:
for cl in match.context_before:
print(f" \033[2m{cl}\033[0m")
# Highlight the matched line
print(f" \033[1m>>> {match.context_before and '' or ''}", end='')
# Find the actual line
try:
with open(match.file, 'r', errors='replace') as f:
for i, line in enumerate(f):
if i == match.line_num - 1:
print(f"{line.rstrip()}\033[0m")
break
except (OSError, PermissionError):
print(f"(could not re-read line)\033[0m")
if match.context_after:
for cl in match.context_after:
print(f" \033[2m{cl}\033[0m")main function · python · L139-L214 (76 LOC)research/tools/repo_analysis/extract_aes_patterns.py
def main():
repos_dir = sys.argv[1] if len(sys.argv) > 1 else REPOS_DIR
if repos_dir in ('-h', '--help'):
print(__doc__.strip())
sys.exit(0)
if not os.path.isdir(repos_dir):
print(f"ERROR: Directory not found: {repos_dir}", file=sys.stderr)
sys.exit(1)
print(f"Scanning {repos_dir} for AES/crypto patterns...")
print(f"Extensions: {', '.join(sorted(SOURCE_EXTENSIONS))}")
print()
all_matches = []
files_scanned = 0
for root, dirs, files in os.walk(repos_dir):
# Skip hidden dirs and common non-code dirs
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in
('node_modules', '__pycache__', '.git', 'build', 'target')]
for fname in files:
ext = os.path.splitext(fname)[1].lower()
if ext not in SOURCE_EXTENSIONS:
continue
filepath = Path(root) / fname
files_scanned += 1
matches = scan_file(filepatRepobility · code-quality intelligence platform · https://repobility.com
XRegistryEntry class · python · L36-L68 (33 LOC)research/tools/repo_analysis/xregistry_tool.py
class XRegistryEntry:
"""Represents a single xRegistry entry."""
def __init__(self, offset: int, entry_type: int, path: str,
data_offset: int = 0, data_size: int = 0, data: bytes = b''):
self.offset = offset
self.entry_type = entry_type
self.path = path
self.data_offset = data_offset
self.data_size = data_size
self.data = data
def type_name(self) -> str:
return {1: 'DIR', 2: 'INT', 3: 'BIN'}.get(self.entry_type, f'UNK({self.entry_type})')
def value_str(self) -> str:
if self.entry_type == ENTRY_TYPE_INT and len(self.data) >= 4:
val = struct.unpack('>I', self.data[:4])[0]
return f'{val} (0x{val:08x})'
elif self.entry_type == ENTRY_TYPE_BIN and self.data:
hex_str = self.data.hex()
if len(hex_str) > 64:
hex_str = hex_str[:64] + '...'
# Also try ASCII
try:
ascii_str = self.data.decod__init__ method · python · L38-L45 (8 LOC)research/tools/repo_analysis/xregistry_tool.py
def __init__(self, offset: int, entry_type: int, path: str,
data_offset: int = 0, data_size: int = 0, data: bytes = b''):
self.offset = offset
self.entry_type = entry_type
self.path = path
self.data_offset = data_offset
self.data_size = data_size
self.data = datavalue_str method · python · L50-L68 (19 LOC)research/tools/repo_analysis/xregistry_tool.py
def value_str(self) -> str:
if self.entry_type == ENTRY_TYPE_INT and len(self.data) >= 4:
val = struct.unpack('>I', self.data[:4])[0]
return f'{val} (0x{val:08x})'
elif self.entry_type == ENTRY_TYPE_BIN and self.data:
hex_str = self.data.hex()
if len(hex_str) > 64:
hex_str = hex_str[:64] + '...'
# Also try ASCII
try:
ascii_str = self.data.decode('ascii').rstrip('\x00')
if ascii_str.isprintable() and len(ascii_str) > 2:
return f'{hex_str} (ascii: "{ascii_str}")'
except (UnicodeDecodeError, ValueError):
pass
return hex_str
elif self.entry_type == ENTRY_TYPE_DIR:
return '<directory>'
return '<empty>'XRegistryParser class · python · L71-L257 (187 LOC)research/tools/repo_analysis/xregistry_tool.py
class XRegistryParser:
"""Parse PS3 xRegistry.sys binary format."""
def __init__(self, filepath: str):
self.filepath = filepath
with open(filepath, 'rb') as f:
self.data = bytearray(f.read())
self.entries = []
self._parse()
def _parse(self):
"""Parse the xRegistry structure."""
data = self.data
# Verify magic
if data[:4] != XREG_MAGIC:
# Try scanning for the magic
pos = data.find(XREG_MAGIC)
if pos == -1:
print(f"WARNING: xRegistry magic {XREG_MAGIC.hex()} not found at offset 0",
file=sys.stderr)
# Fall back to string scanning
self._parse_by_paths()
return
else:
print(f"NOTE: Magic found at offset 0x{pos:x} (expected 0x0)", file=sys.stderr)
# Parse header
# Typical header: magic(4) + version(2) + entry_count(2) + data_offset(4) + ..__init__ method · python · L74-L79 (6 LOC)research/tools/repo_analysis/xregistry_tool.py
def __init__(self, filepath: str):
self.filepath = filepath
with open(filepath, 'rb') as f:
self.data = bytearray(f.read())
self.entries = []
self._parse()_parse method · python · L81-L113 (33 LOC)research/tools/repo_analysis/xregistry_tool.py
def _parse(self):
"""Parse the xRegistry structure."""
data = self.data
# Verify magic
if data[:4] != XREG_MAGIC:
# Try scanning for the magic
pos = data.find(XREG_MAGIC)
if pos == -1:
print(f"WARNING: xRegistry magic {XREG_MAGIC.hex()} not found at offset 0",
file=sys.stderr)
# Fall back to string scanning
self._parse_by_paths()
return
else:
print(f"NOTE: Magic found at offset 0x{pos:x} (expected 0x0)", file=sys.stderr)
# Parse header
# Typical header: magic(4) + version(2) + entry_count(2) + data_offset(4) + ...
# The exact format varies; we'll try the common layout
if len(data) < 16:
print("ERROR: File too small", file=sys.stderr)
return
# Try to read entry table
# Format hypothesis: after header, entries are sequential with:
_parse_by_paths method · python · L115-L184 (70 LOC)research/tools/repo_analysis/xregistry_tool.py
def _parse_by_paths(self):
"""Parse by finding all /setting/ paths in the file."""
data = self.data
pos = 0
while pos < len(data):
# Find next path-like string
idx = data.find(b'/setting/', pos)
if idx == -1:
idx = data.find(b'/setting2/', pos)
if idx == -1:
break
# Read the full path (null-terminated)
end = data.find(b'\x00', idx)
if end == -1 or end - idx > 256:
pos = idx + 1
continue
path = data[idx:end].decode('ascii', errors='replace')
# Look at bytes before the path for entry metadata
# Typical: a few header bytes before the path string
entry_header_start = max(0, idx - 8)
header_bytes = data[entry_header_start:idx]
# Try to determine entry type from header
entry_type = 0
data_offset = 0
list_entries method · python · L186-L190 (5 LOC)research/tools/repo_analysis/xregistry_tool.py
def list_entries(self, filter_path: Optional[str] = None) -> list:
"""List all entries, optionally filtered by path prefix."""
if filter_path:
return [e for e in self.entries if filter_path in e.path]
return self.entriesRepobility analyzer · published findings · https://repobility.com
read_entry method · python · L192-L197 (6 LOC)research/tools/repo_analysis/xregistry_tool.py
def read_entry(self, path: str) -> Optional[XRegistryEntry]:
"""Read a specific entry by exact path."""
for e in self.entries:
if e.path == path:
return e
return Nonesearch_entries method · python · L199-L202 (4 LOC)research/tools/repo_analysis/xregistry_tool.py
def search_entries(self, term: str) -> list:
"""Search entries by path substring."""
term_lower = term.lower()
return [e for e in self.entries if term_lower in e.path.lower()]inject_value method · python · L204-L239 (36 LOC)research/tools/repo_analysis/xregistry_tool.py
def inject_value(self, path: str, value_hex: str, output_path: str) -> bool:
"""Inject a value at a given path and write to output file."""
value_bytes = bytes.fromhex(value_hex.replace(' ', ''))
entry = self.read_entry(path)
if entry is None:
print(f"ERROR: Path not found: {path}", file=sys.stderr)
print("Available premo paths:", file=sys.stderr)
for e in self.search_entries('premo'):
print(f" {e.path}", file=sys.stderr)
return False
# Find where to write the data
# The data location depends on the entry format
# For now, we'll write at the data_offset if known,
# or right after the null terminator of the path
write_offset = entry.data_offset if entry.data_offset else (
entry.offset + len(entry.path.encode('ascii')) + 1
)
if write_offset + len(value_bytes) > len(self.data):
print(f"ERROR: Write would exc