#!/usr/bin/env python3 """Trova i call site di funzioni importate dentro un .sprx PS5. In un ET_SCE_DYNAMIC le chiamate alle librerie esterne passano dalla PLT, e la PLT e' anonima (nessun nome, nessuna sezione). Ma ogni stub PLT e' un `jmp *disp32(%rip)` che punta a uno slot GOT, e quello slot e' descritto da una rilocazione in .rela.plt il cui indice di simbolo punta al dynsym: li' c'e' il NID. Quindi: rilocazione .rela.plt -> (slot GOT -> NID) scansione `ff 25` -> (stub PLT -> slot GOT) scansione `e8` -> (call site -> stub PLT) Da li' si legge come il chiamante riempie le strutture: molto piu' veloce che ricostruirle a tentativi sulla console. """ import struct import sys import os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import sprx as sprxmod def parse_full(path): d = open(path, 'rb').read() phoff = struct.unpack_from(' NID, dall'insieme delle rilocazioni PLT.""" d, v2f, tag = r['data'], r['v2f'], r['tag'] jmprel = tag.get(0x17) # DT_JMPREL pltrelsz = tag.get(2) or tag.get(0x2b) # DT_PLTRELSZ (a volte tag 2) out = {} if not jmprel or not pltrelsz: return out off = v2f(jmprel) if off is None: off = jmprel for i in range(pltrelsz // 24): o = off + i * 24 r_offset, r_info, r_addend = struct.unpack_from('> 32 if symidx < len(r['symnames']): name = r['symnames'][symidx] nid = name.split('#')[0] if '#' in name else name out[r_offset] = nid return out def scan(r): """Ritorna (stub PLT -> NID) e (call site -> stub PLT).""" d, v2f, ph = r['data'], r['v2f'], r['ph'] got2nid = plt_map(r) exec_segs = [p for p in ph if p[0] == 1 and (p[1] & 1)] stub2nid = {} call2stub = [] for t, fl, off, va, fsz, msz in exec_segs: code = d[off:off + fsz] # jmp *disp32(%rip) = ff 25 xx xx xx xx for i in range(len(code) - 6): if code[i] == 0xff and code[i + 1] == 0x25: disp = struct.unpack_from(' %-12s %s' % (a, nid, want_nids[nid])) print('--- call site ---') for a, tgt in calls: nid = stub2nid.get(tgt) if not nid or nid not in want_nids: continue print() print(' ### %s chiamata da 0x%x' % (want_nids[nid], a)) # decodifica il contesto attorno alla chiamata off = v2f(a) if off is None: continue start = max(0, off - ctx * 8) lines = [] i = start while i < off + 8: b = d[i:i + 1] lines.append('%08x %s' % (i, ' '.join('%02x' % c for c in d[i:i + 8]))) i += 8 for l in lines: print(' ' + l) if __name__ == '__main__': want = { 'svzPXluOz8U': 'sceAvcap2Initialize', 'gojqghDU+1Y': 'sceAvcap2Terminate', '4jwUgirLFrA': 'sceAvcap2OpenVideo', '8CGNCwBsItI': 'sceAvcap2Start', 'pN0hIQZsMzc': 'sceAvcap2ReadVideo', 'jkv9MlN7gvs': 'sceAvcap2ReadVideo2', 'tR7gPe1i8hw': 'sceAvcap2Close', 'YaXDckFygvw': 'sceAvcap2RequestKeyFrame', 'Dht3tcbRoEY': 'sceAvcap2QueryVideoEncoderMemorySize', 'X8c-f8LgzHo': 'sceAvcap2MapMemory', '2GC2UnBJHIA': 'sceAvcap2GetFramePitch', 'RW88AiwmL+Y': 'sceAvcap2GetSupportInformation', '-NotZDC2pQc': 'sceAvcap2SetVideoEncodeConfig', 'NeKlrUv+lbY': 'sceAvcap2GetNonVclNalUnitStream', 'CYgd06bO5q0': 'sceAvcap2AllocateVideoEncoderMemory', 'j7ClJpPRVQI': 'sceAvcap2GetImagePasteMemory', } for p in sys.argv[1:]: report(p, want)