Post APnQUtxQBzhBiKJ4lc by Rairii@infosec.exchange
(DIR) More posts by Rairii@infosec.exchange
(DIR) Post #APnQUtxQBzhBiKJ4lc by Rairii@infosec.exchange
2022-11-20T11:44:55Z
1 likes, 0 repeats
So here's some interesting #RaspberryPi related #ReverseEngineering research.Did you know that there's another undocumented microcontroller core in the BCM2708 SoC and its derivates?Around 2019 or so, I found this set of slides : https://www.rump.beer/2017/slides/etude_d_un_CPU_inconnu.pdfThe really fun stuff was redacted (I can only assume how they were convinced to remove the stuff related to keygenning the codec licensing :thinking: ), but that didn't matter. With some hardware experimentation, I figured out the majority of the remaining instruction set and coded a disassembler for the Broadcom Video Control Engine (VCE).I never did figure out the entire instruction set, but hopefully others can build upon my research.Yes, I coded a disassembler in PHP, it's something all Real Programmers should do, highly recommend.<?php// thanks synacktiv :)// todo: figure out the full instruction set, eventually$opTbl = [ 0b000000 => 'end', 0b000001 => 'xor', 0b000010 => 'sendc', // set end code? 0b000011 => 'dmarun', // start dma? dmarun blank,blank,1 used in prerun/postrun// 0b000100 => 'UNK', // rD not touched.// 0b000101 => 'UNK', // rD not touched.// 0b000110 => 'UNK', // rD not touched.// 0b000111 => 'UNK', // rD not touched. // load: op[0] = *(TYPE*)(op[1]) 0b001000 => 'ldb', 0b001001 => 'ldw', 0b001010 => 'ldd', 0b001011 => 'ver', // << output reg gets 0x68323634 'h264' // store: *(TYPE*)(op[0]) = op[2]; 0b001100 => 'stb', 0b001101 => 'stw', 0b001110 => 'std',// 0b001111 => 'UNK', // << unsure what this does. used by h264_code 0b010000 => 'dmafr', // set main memory addr to dma FROM? dmaf r1, <reg>, 0 used by prerun -- dest always at 0 of vce code/data? 0b010001 => 'dmato', // set main memory addr to dma TO? dmato r1, <reg>, 0 used by postrun; src always at 0? 0b010010 => 'dmatdl', // set length of copy to dma to DATA? mov r6, <len>;mov r7,0;dmadl r1, r7, 0 used by prerun 0b010011 => 'dmafdl', // set length of copy to dma from DATA?// 0b010100 => 'UNK', // op[0] = (op[1] << op[2]) with maybe some additional bits set in low4// 0b010101 => 'UNK', // op[0] = op[1] unk op[2], something bitwise? shl-related?// 0b010110 => 'UNK', // rD not touched.// 0b010111 => 'UNK', // rD not touched. 0b011000 => 'zero', // op[0] = 0 0b011001 => 'zero', // op[0] = 0// 0b011010 => 'UNK', // rD not touched. 0b011011 => 'dmacl', // set length of copy to dma to CODE? mov r6, <len>;mov r7,0;dmacl r1, r7, 0 used by prerun 0b011100 => 'j', 0b011101 => 'ldiw', // << op[0] = *(u16*)(op[2]), if not aligned does the same thing as mem-operand// 0b011110 => 'UNK', // << rD not touched.// 0b011111 => 'UNK', // << rD not touched. 0b100000 => 'shl', // op[0]=op[1]<<op[2] 0b100001 => 'rshl', // op[0]=op[2]<<op[1] 0b100010 => 'shr', 0b100011 => 'mov', // << op[0]=op[1] 0b100100 => 'shr', 0b100101 => 'movi', // op[0] = op[2] 0b100110 => 'ror', // op[0] = op[1] ror op[2] 0b100111 => 'movi', 0b101000 => 'mulu', // op[0] = op[1] * op[2], all unsigned, 24-bit output?// 0b101001 => 'UNK', // op[0] = op[1] <UNK> op[2], something bitwise? 0b101010 => 'muls', // op[0] = op[1] * op[2], all signed, 24-bit output?// 0b101011 => 'UNK', // op[0] = op[1] <UNK> op[2], something bitwise? 0b101100 => 'add', 0b101101 => 'sub', 0b101110 => 'ldid', // op[0] = *(u32*)(op[2] & ~3) 0b101111 => 'rsub', // << op[0]=op[2]-op[1] 0b110000 => 'sub1', // op[0] = op[1] - 1 - op[2] 0b110001 => 'and', 0b110010 => 'bic', // op[0] = op[1] & ~op[2] 0b110011 => 'xorlim', // op[0] = (op[1] ^ op[2]) & mask(bnh(op[2])) 0b110100 => 'or', // op[0] = op[1] | op[2] 0b110101 => 'xor',// 0b110110 => 'UNK', // op[0] = op[1] <UNK> op[2], some bitwise operation...// 0b110111 => 'UNK', // op[0] = op[2] << unk(op[1]) 0b111000 => 'signext', // op[0] = sign_extend(op[1] & mask(op[2])) 0b111001 => 'movi', // op[0] = op[2] 0b111010 => 'mov', 0b111011 => 'bnh', // op[0] = number of highest bit set in op[1], so bnh(0xffffffff)=31, bnh(0x7fffffff)=30 etc. 0b111100 => 'cmp',// 0b111101 => 'UNK', // cmp variant? seen used before conditional instrs// 0b111110 => 'UNK',// 0b111111 => 'UNK'];$condTbl = [ 0b00 => '',// 0b01 => 0b10 => 'eq', 0b11 => 'ne',];function registerParse($reg) { if ($reg == 0) return 'last'; if ($reg == 63) return 'blank'; return sprintf('r%d', $reg);}function operandParse($imm) { if (($imm >> 7) == 0b01101) { // reg? $reg = ($imm >> 3) & 0b1111; return registerParse($reg); } if (($imm & 0x800) == 0x800) { // (u32) data return sprintf('(0x%x)', $imm & 0x7ff); } if ($imm > 32) return sprintf('0x%x', $imm); return sprintf('%d', $imm);}function opCodeAsText($opcode) { global $opTbl, $condTbl; $ret = ''; if (in_array($opcode >> 2, array_keys($opTbl))) $ret = $opTbl[$opcode >> 2]; else { $opBin = decbin($opcode >> 2); if (strlen($opBin) < 6) $opBin = str_repeat('0',6 - strlen($opBin)) . $opBin; $ret = 'unk_0b' . $opBin; } $cond = $opcode & 0b11; if (in_array($cond, array_keys($condTbl))) $condTxt = $condTbl[$cond]; else { $condBin = decbin($cond); if (strlen($condBin) < 2) $condBin = str_repeat('0', 2 - strlen($condBin)) . $condBin; $condTxt = 'unk_0b' . $condBin; } if ($condTxt != "") $ret .= '.' . $condTxt; return $ret;}function unpackOp($op) { // bits: // 0-11: immediate // 12-17: register operand 2 // 18-23: register operand 1 // 24-31: opcode return (object) [ 'opcode' => ( ($op >> 24) & 0b11111111 ), 'reg1' => ( ($op >> 18) & 0b111111 ), 'reg2' => ( ($op >> 12) & 0b111111 ), 'imm' => ( $op & 0b111111111111 ), 'imm_ex' => ( $op & 0b111111111111111111 ) ];}function dis($bytes) { $arr = unpack('V*', $bytes); $off = 0; foreach ($arr as $u32) { $op = unpackOp($u32); $reg1 = registerParse($op->reg1); if ($reg1 != "") $reg1 .= ', '; $reg2 = registerParse($op->reg2); if ($reg2 != "") $reg2 .= ', '; $operands = trim(sprintf("%s%s%s", $reg1, $reg2, operandParse($op->imm)), ","); printf("%08x : %08x : %s %s\n",$off,$u32,opCodeAsText($op->opcode),$operands); $off+=4; }}if ($argc == 0) die();dis( file_get_contents( $argv[1] ) );