#!/usr/bin/env python3 """Minimal PowerPC disassembler for /image/sylpheed.pe. tools/ppc-dis disassemble a range tools/ppc-dis --find-imm [lo hi] every instruction with that immediate The image is a FLAT VA DUMP: file offset = VA - 0x82000000. There is no `duckdb` and no PowerPC `objdump` in this container, so this is the working route to the bytes -- and the bytes are primary, the database is somebody's analysis of them. ⚠️ It lives here rather than in /tmp because the scratchpad is wiped by container restarts, which cost this tool three times in one session. Covers the integer/branch/load-store forms this corpus actually reads. Floating point is printed as `FP ... xo=` rather than decoded: no finding so far has needed an FP mnemonic, and a wrong one would be worse than an honest placeholder. """ import struct, sys BASE = 0x82000000 IMG = "/image/sylpheed.pe" def _mask(mb, me): m, i = 0, mb while True: m |= 1 << (31 - i) if i == me: break i = (i + 1) & 31 return m XO = {0:'cmpw',32:'cmplw',28:'and',444:'or',316:'xor',60:'andc',266:'add',40:'subf', 104:'neg',24:'slw',536:'srw',824:'srawi',23:'lwzx',87:'lbzx',279:'lhzx', 151:'stwx',339:'mfspr',467:'mtspr',235:'mullw',491:'divw',459:'divwu', 922:'extsh',954:'extsb',407:'sthx',215:'stbx',343:'lhax',55:'lwzux', 20:'lwarx',150:'stwcx.',412:'orc',476:'nand',124:'nor',8:'subfc',10:'addc', 138:'adde',26:'cntlzw',792:'sraw'} DF = {32:'lwz',33:'lwzu',34:'lbz',35:'lbzu',36:'stw',37:'stwu',38:'stb',39:'stbu', 40:'lhz',41:'lhzu',42:'lha',44:'sth',45:'sthu',14:'addi',15:'addis', 12:'addic',13:'addic.',7:'mulli',8:'subfic',24:'ori',25:'oris',26:'xori', 27:'xoris',28:'andi.',29:'andis.',10:'cmpli',11:'cmpi',48:'lfs',50:'lfd', 52:'stfs',54:'stfd'} FPOPS = {48, 49, 50, 51, 52, 53, 54, 55} def dis(va, x): op = x >> 26; rD = (x >> 21) & 31; rA = (x >> 16) & 31; rB = (x >> 11) & 31 imm = x & 0xFFFF; s = imm - 0x10000 if imm & 0x8000 else imm if op in DF: m = DF[op]; r = 'f' if op in FPOPS else 'r' if m in ('addi', 'addis') and rA == 0: return "li r%d,%d" % (rD, s) if m in ('ori', 'oris', 'xori', 'xoris', 'andi.', 'andis.'): return "%s r%d,r%d,0x%X" % (m, rA, rD, imm) if m in ('cmpli', 'cmpi'): return "%s cr%d,r%d,%s" % (m, (x >> 23) & 7, rA, hex(imm) if m == 'cmpli' else s) if m in ('addi', 'addis', 'addic', 'addic.', 'mulli', 'subfic'): return "%s r%d,r%d,%d" % (m, rD, rA, s) return "%s %s%d,%d(r%d)" % (m, r, rD, s, rA) if op in (20, 21): S, A, SH, MB, ME = rD, rA, (x >> 11) & 31, (x >> 6) & 31, (x >> 1) & 31 return "%s r%d,r%d,%d,%d,%d ; mask=0x%08X" % ( 'rlwimi' if op == 20 else 'rlwinm', A, S, SH, MB, ME, _mask(MB, ME)) if op == 31: e = (x >> 1) & 0x3FF; m = XO.get(e) if m is None: return ".long 0x%08X ; op31 xo=%d" % (x, e) if m in ('cmpw', 'cmplw'): return "%s cr%d,r%d,r%d" % (m, (x >> 23) & 7, rA, rB) if m == 'srawi': return "srawi r%d,r%d,%d" % (rA, rD, rB) if m in ('or', 'and', 'xor', 'andc', 'orc', 'nand', 'nor', 'slw', 'srw', 'sraw'): return "%s r%d,r%d,r%d" % (m, rA, rD, rB) if m in ('extsh', 'extsb', 'neg', 'cntlzw'): return "%s r%d,r%d" % (m, rA, rD) return "%s r%d,r%d,r%d" % (m, rD, rA, rB) if op == 18: li = x & 0x03FFFFFC if li & 0x02000000: li -= 0x04000000 return "b%s 0x%08X" % ('l' if x & 1 else '', (va + li) & 0xFFFFFFFF) if op == 16: bo, bi = rD, rA bd = x & 0xFFFC if bd & 0x8000: bd -= 0x10000 cond = {(12,0):'blt',(12,1):'bgt',(12,2):'beq',(4,0):'bge',(4,1):'ble',(4,2):'bne'} nm = cond.get((bo, bi & 3)) or cond.get((bo & 0x1E, bi & 3)) or "bc(%d,%d)" % (bo, bi) return "%s cr%d,0x%08X" % (nm, bi >> 2, (va + bd) & 0xFFFFFFFF) if op == 19: return {16: 'blr', 528: 'bctr'}.get((x >> 1) & 0x3FF, ".long 0x%08X" % x) if op in (59, 63): return "FP%d rD=%d rA=%d rB=%d rC=%d xo=%d" % (op, rD, rA, rB, (x >> 6) & 31, (x >> 1) & 0x1F) return ".long 0x%08X ; op=%d" % (x, op) def main(argv): d = open(IMG, 'rb').read() if argv[0] == '--find-imm': want = int(argv[1], 0) lo = int(argv[2], 16) if len(argv) > 2 else BASE hi = int(argv[3], 16) if len(argv) > 3 else BASE + len(d) for a in range(lo, hi, 4): x = struct.unpack_from('>I', d, a - BASE)[0] if (x & 0xFFFF) == (want & 0xFFFF) and (x >> 26) in DF: print("%08X %08X %s" % (a, x, dis(a, x))) return 0 a, e = int(argv[0], 16), int(argv[1], 16) while a < e: x = struct.unpack_from('>I', d, a - BASE)[0] print("%08X %08X %s" % (a, x, dis(a, x))) a += 4 return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:]))