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] ) );