Friday, 9 December 2011

Beating the GCHQ challenge (part 2)

So after the let-down of having to complete another test after the last one, I found myself with the following javascript file:



//--------------------------------------------------------------------------------------------------
//
// stage 2 of 3
//
// challenge:
//   reveal the solution within VM.mem
//
// disclaimer:
//   tested in ie 9, firefox 6, chrome 14 and v8 shell (http://code.google.com/apis/v8/build.html),
//   other javascript implementations may or may not work.
//
//--------------------------------------------------------------------------------------------------


var VM = {
 
    cpu: {
        ip: 0x00,
   
        r0: 0x00,
        r1: 0x00,
        r2: 0x00,
        r3: 0x00,
   
        cs: 0x00,
        ds: 0x10,
   
        fl: 0x00,
   
        firmware: [0xd2ab1f05, 0xda13f110]
    },
 
    mem: [
        0x31, 0x04, 0x33, 0xaa, 0x40, 0x02, 0x80, 0x03, 0x52, 0x00, 0x72, 0x01, 0x73, 0x01, 0xb2, 0x50,
        0x30, 0x14, 0xc0, 0x01, 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       
        0x98, 0xab, 0xd9, 0xa1, 0x9f, 0xa7, 0x83, 0x83, 0xf2, 0xb1, 0x34, 0xb6, 0xe4, 0xb7, 0xca, 0xb8,
        0xc9, 0xb8, 0x0e, 0xbd, 0x7d, 0x0f, 0xc0, 0xf1, 0xd9, 0x03, 0xc5, 0x3a, 0xc6, 0xc7, 0xc8, 0xc9,
        0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
        0xda, 0xdb, 0xa9, 0xcd, 0xdf, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
        0x26, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
        0x7d, 0x1f, 0x15, 0x60, 0x4d, 0x4d, 0x52, 0x7d, 0x0e, 0x27, 0x6d, 0x10, 0x6d, 0x5a, 0x06, 0x56,
        0x47, 0x14, 0x42, 0x0e, 0xb6, 0xb2, 0xb2, 0xe6, 0xeb, 0xb4, 0x83, 0x8e, 0xd7, 0xe5, 0xd4, 0xd9,
        0xc3, 0xf0, 0x80, 0x95, 0xf1, 0x82, 0x82, 0x9a, 0xbd, 0x95, 0xa4, 0x8d, 0x9a, 0x2b, 0x30, 0x69,
        0x4a, 0x69, 0x65, 0x55, 0x1c, 0x7b, 0x69, 0x1c, 0x6e, 0x04, 0x74, 0x35, 0x21, 0x26, 0x2f, 0x60,
        0x03, 0x4e, 0x37, 0x1e, 0x33, 0x54, 0x39, 0xe6, 0xba, 0xb4, 0xa2, 0xad, 0xa4, 0xc5, 0x95, 0xc8,
        0xc1, 0xe4, 0x8a, 0xec, 0xe7, 0x92, 0x8b, 0xe8, 0x81, 0xf0, 0xad, 0x98, 0xa4, 0xd0, 0xc0, 0x8d,
        0xac, 0x22, 0x52, 0x65, 0x7e, 0x27, 0x2b, 0x5a, 0x12, 0x61, 0x0a, 0x01, 0x7a, 0x6b, 0x1d, 0x67,
        0x75, 0x70, 0x6c, 0x1b, 0x11, 0x25, 0x25, 0x70, 0x7f, 0x7e, 0x67, 0x63, 0x30, 0x3c, 0x6d, 0x6a,
        0x01, 0x51, 0x59, 0x5f, 0x56, 0x13, 0x10, 0x43, 0x19, 0x18, 0xe5, 0xe0, 0xbe, 0xbf, 0xbd, 0xe9,
        0xf0, 0xf1, 0xf9, 0xfa, 0xab, 0x8f, 0xc1, 0xdf, 0xcf, 0x8d, 0xf8, 0xe7, 0xe2, 0xe9, 0x93, 0x8e,
        0xec, 0xf5, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   
        0x37, 0x7a, 0x07, 0x11, 0x1f, 0x1d, 0x68, 0x25, 0x32, 0x77, 0x1e, 0x62, 0x23, 0x5b, 0x47, 0x55,
        0x53, 0x30, 0x11, 0x42, 0xf6, 0xf1, 0xb1, 0xe6, 0xc3, 0xcc, 0xf8, 0xc5, 0xe4, 0xcc, 0xc0, 0xd3,
        0x85, 0xfd, 0x9a, 0xe3, 0xe6, 0x81, 0xb5, 0xbb, 0xd7, 0xcd, 0x87, 0xa3, 0xd3, 0x6b, 0x36, 0x6f,
        0x6f, 0x66, 0x55, 0x30, 0x16, 0x45, 0x5e, 0x09, 0x74, 0x5c, 0x3f, 0x29, 0x2b, 0x66, 0x3d, 0x0d,
        0x02, 0x30, 0x28, 0x35, 0x15, 0x09, 0x15, 0xdd, 0xec, 0xb8, 0xe2, 0xfb, 0xd8, 0xcb, 0xd8, 0xd1,
        0x8b, 0xd5, 0x82, 0xd9, 0x9a, 0xf1, 0x92, 0xab, 0xe8, 0xa6, 0xd6, 0xd0, 0x8c, 0xaa, 0xd2, 0x94,
        0xcf, 0x45, 0x46, 0x67, 0x20, 0x7d, 0x44, 0x14, 0x6b, 0x45, 0x6d, 0x54, 0x03, 0x17, 0x60, 0x62,
        0x55, 0x5a, 0x4a, 0x66, 0x61, 0x11, 0x57, 0x68, 0x75, 0x05, 0x62, 0x36, 0x7d, 0x02, 0x10, 0x4b,
        0x08, 0x22, 0x42, 0x32, 0xba, 0xe2, 0xb9, 0xe2, 0xd6, 0xb9, 0xff, 0xc3, 0xe9, 0x8a, 0x8f, 0xc1,
        0x8f, 0xe1, 0xb8, 0xa4, 0x96, 0xf1, 0x8f, 0x81, 0xb1, 0x8d, 0x89, 0xcc, 0xd4, 0x78, 0x76, 0x61,
        0x72, 0x3e, 0x37, 0x23, 0x56, 0x73, 0x71, 0x79, 0x63, 0x7c, 0x08, 0x11, 0x20, 0x69, 0x7a, 0x14,
        0x68, 0x05, 0x21, 0x1e, 0x32, 0x27, 0x59, 0xb7, 0xcf, 0xab, 0xdd, 0xd5, 0xcc, 0x97, 0x93, 0xf2,
        0xe7, 0xc0, 0xeb, 0xff, 0xe9, 0xa3, 0xbf, 0xa1, 0xab, 0x8b, 0xbb, 0x9e, 0x9e, 0x8c, 0xa0, 0xc1,
        0x9b, 0x5a, 0x2f, 0x2f, 0x4e, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    ],
 
    exec: function()
    {
    // virtual machine architecture
    // ++++++++++++++++++++++++++++
    //
    // segmented memory model with 16-byte segment size (notation seg:offset)
    //
    // 4 general-purpose registers (r0-r3)
    // 2 segment registers (cs, ds equiv. to r4, r5)
    // 1 flags register (fl)
    //
    // instruction encoding
    // ++++++++++++++++++++
    //
    //           byte 1               byte 2 (optional)
    // bits      [ 7 6 5 4 3 2 1 0 ]  [ 7 6 5 4 3 2 1 0 ]
    // opcode      - - -            
    // mod               -          
    // operand1            - - - -
    // operand2                         - - - - - - - -
    //
    // operand1 is always a register index
    // operand2 is optional, depending upon the instruction set specified below
    // the value of mod alters the meaning of any operand2
    //   0: operand2 = reg ix
    //   1: operand2 = fixed immediate value or target segment (depending on instruction)
    //
    // instruction set
    // +++++++++++++++
    //
    // Notes:
    //   * r1, r2 => operand 1 is register 1, operand 2 is register 2
    //   * movr r1, r2 => move contents of register r2 into register r1
    //
    // opcode | instruction | operands (mod 0) | operands (mod 1)
    // -------+-------------+------------------+-----------------
    // 0x00   | jmp         | r1               | r2:r1
    // 0x01   | movr        | r1, r2           | rx,   imm
    // 0x02   | movm        | r1, [ds:r2]      | [ds:r1], r2
    // 0x03   | add         | r1, r2           | r1,   imm
    // 0x04   | xor         | r1, r2           | r1,   imm
    // 0x05   | cmp         | r1, r2           | r1,   imm
    // 0x06   | jmpe        | r1               | r2:r1
    // 0x07   | hlt         | N/A              | N/A
    //
    // flags
    // +++++
    //
    // cmp r1, r2 instruction results in:
    //   r1 == r2 => fl = 0
    //   r1 < r2  => fl = 0xff
    //   r1 > r2  => fl = 1
    //
    // jmpe r1
    //   => if (fl == 0) jmp r1
    //      else nop
   
        throw "VM.exec not yet implemented";
    }
 
};


//--------------------------------------------------------------------------------------------------


try
{
    VM.exec();
}
catch(e)
{
    alert('\nError: ' + e + '\n');
}


//--------------------------------------------------------------------------------------------------
 


So we have the details of an instruction set, three segments of memory and some variables that represent registers (oh...and some suspicious looking firmware values!).

The task here is to write a simple disassembler, be able to store a set of decoded instructions and to then execute them as part of a virtual machine. The tricky part is that the first section of code decodes the next one, so you must disassemble, build the instructions, run, disassemble, build more instructions then run again. After this you find the following in the second segment of memory:

'G' , 'E' , 'T' , ' ' , '/' , 'd' , 'a' , '7' , '5' , '3' , '7' , '0' , 'f' , 'e' , '1' , '5' ,
'c' , '4' , '1' , '4' , '8' , 'b' , 'd' , '4' , 'c' , 'e' , 'e' , 'c' , '8' , '6' , '1' , 'f' ,
'b' , 'd' , 'a' , 'a' , '5' , '.' , 'e' , 'x' , 'e' , ' ' , 'H' , 'T' , 'T' , 'P' , '/' , '1' ,

This is the address for the next stage of the challenge.

Interestingly, the third segment seems unchanged - very suspicious as there's not much spare information in any of these challenges.

My final code for this stage:



//--------------------------------------------------------------------------------------------------
//
// stage 2 of 3
//
// challenge:
//   reveal the solution within VM.mem
//
// disclaimer:
//   tested in ie 9, firefox 6, chrome 14 and v8 shell (http://code.google.com/apis/v8/build.html),
//   other javascript implementations may or may not work.
//
//--------------------------------------------------------------------------------------------------


var VM = {  
        cpu: {
        ip: 0x00,
       
        r0: 0x00,
        r1: 0x00,
        r2: 0x00,
        r3: 0x00,
       
        cs: 0x00,
        ds: 0x10,
     
        fl: 0x00,
     
        firmware: [0xd2ab1f05, 0xda13f110]
    },
   
    mem: [
        0x31, 0x04, 0x33, 0xaa, 0x40, 0x02, 0x80, 0x03, 0x52, 0x00, 0x72, 0x01, 0x73, 0x01, 0xb2, 0x50,
        0x30, 0x14, 0xc0, 0x01, 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       
        0x98, 0xab, 0xd9, 0xa1, 0x9f, 0xa7, 0x83, 0x83, 0xf2, 0xb1, 0x34, 0xb6, 0xe4, 0xb7, 0xca, 0xb8,
        0xc9, 0xb8, 0x0e, 0xbd, 0x7d, 0x0f, 0xc0, 0xf1, 0xd9, 0x03, 0xc5, 0x3a, 0xc6, 0xc7, 0xc8, 0xc9,
        0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
        0xda, 0xdb, 0xa9, 0xcd, 0xdf, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
        0x26, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
        0x7d, 0x1f, 0x15, 0x60, 0x4d, 0x4d, 0x52, 0x7d, 0x0e, 0x27, 0x6d, 0x10, 0x6d, 0x5a, 0x06, 0x56,
        0x47, 0x14, 0x42, 0x0e, 0xb6, 0xb2, 0xb2, 0xe6, 0xeb, 0xb4, 0x83, 0x8e, 0xd7, 0xe5, 0xd4, 0xd9,
        0xc3, 0xf0, 0x80, 0x95, 0xf1, 0x82, 0x82, 0x9a, 0xbd, 0x95, 0xa4, 0x8d, 0x9a, 0x2b, 0x30, 0x69,
        0x4a, 0x69, 0x65, 0x55, 0x1c, 0x7b, 0x69, 0x1c, 0x6e, 0x04, 0x74, 0x35, 0x21, 0x26, 0x2f, 0x60,
        0x03, 0x4e, 0x37, 0x1e, 0x33, 0x54, 0x39, 0xe6, 0xba, 0xb4, 0xa2, 0xad, 0xa4, 0xc5, 0x95, 0xc8,
        0xc1, 0xe4, 0x8a, 0xec, 0xe7, 0x92, 0x8b, 0xe8, 0x81, 0xf0, 0xad, 0x98, 0xa4, 0xd0, 0xc0, 0x8d,
        0xac, 0x22, 0x52, 0x65, 0x7e, 0x27, 0x2b, 0x5a, 0x12, 0x61, 0x0a, 0x01, 0x7a, 0x6b, 0x1d, 0x67,
        0x75, 0x70, 0x6c, 0x1b, 0x11, 0x25, 0x25, 0x70, 0x7f, 0x7e, 0x67, 0x63, 0x30, 0x3c, 0x6d, 0x6a,
        0x01, 0x51, 0x59, 0x5f, 0x56, 0x13, 0x10, 0x43, 0x19, 0x18, 0xe5, 0xe0, 0xbe, 0xbf, 0xbd, 0xe9,
        0xf0, 0xf1, 0xf9, 0xfa, 0xab, 0x8f, 0xc1, 0xdf, 0xcf, 0x8d, 0xf8, 0xe7, 0xe2, 0xe9, 0x93, 0x8e,
        0xec, 0xf5, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       
        0x37, 0x7a, 0x07, 0x11, 0x1f, 0x1d, 0x68, 0x25, 0x32, 0x77, 0x1e, 0x62, 0x23, 0x5b, 0x47, 0x55,
        0x53, 0x30, 0x11, 0x42, 0xf6, 0xf1, 0xb1, 0xe6, 0xc3, 0xcc, 0xf8, 0xc5, 0xe4, 0xcc, 0xc0, 0xd3,
        0x85, 0xfd, 0x9a, 0xe3, 0xe6, 0x81, 0xb5, 0xbb, 0xd7, 0xcd, 0x87, 0xa3, 0xd3, 0x6b, 0x36, 0x6f,
        0x6f, 0x66, 0x55, 0x30, 0x16, 0x45, 0x5e, 0x09, 0x74, 0x5c, 0x3f, 0x29, 0x2b, 0x66, 0x3d, 0x0d,
        0x02, 0x30, 0x28, 0x35, 0x15, 0x09, 0x15, 0xdd, 0xec, 0xb8, 0xe2, 0xfb, 0xd8, 0xcb, 0xd8, 0xd1,
        0x8b, 0xd5, 0x82, 0xd9, 0x9a, 0xf1, 0x92, 0xab, 0xe8, 0xa6, 0xd6, 0xd0, 0x8c, 0xaa, 0xd2, 0x94,
        0xcf, 0x45, 0x46, 0x67, 0x20, 0x7d, 0x44, 0x14, 0x6b, 0x45, 0x6d, 0x54, 0x03, 0x17, 0x60, 0x62,
        0x55, 0x5a, 0x4a, 0x66, 0x61, 0x11, 0x57, 0x68, 0x75, 0x05, 0x62, 0x36, 0x7d, 0x02, 0x10, 0x4b,
        0x08, 0x22, 0x42, 0x32, 0xba, 0xe2, 0xb9, 0xe2, 0xd6, 0xb9, 0xff, 0xc3, 0xe9, 0x8a, 0x8f, 0xc1,
        0x8f, 0xe1, 0xb8, 0xa4, 0x96, 0xf1, 0x8f, 0x81, 0xb1, 0x8d, 0x89, 0xcc, 0xd4, 0x78, 0x76, 0x61,
        0x72, 0x3e, 0x37, 0x23, 0x56, 0x73, 0x71, 0x79, 0x63, 0x7c, 0x08, 0x11, 0x20, 0x69, 0x7a, 0x14,
        0x68, 0x05, 0x21, 0x1e, 0x32, 0x27, 0x59, 0xb7, 0xcf, 0xab, 0xdd, 0xd5, 0xcc, 0x97, 0x93, 0xf2,
        0xe7, 0xc0, 0xeb, 0xff, 0xe9, 0xa3, 0xbf, 0xa1, 0xab, 0x8b, 0xbb, 0x9e, 0x9e, 0x8c, 0xa0, 0xc1,
        0x9b, 0x5a, 0x2f, 0x2f, 0x4e, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    ],


    decoded_instructions: {},


    decode_mem: function()
    {
        var pos = this.decode_pos;
        var b1 = this.mem[(this.decode_offset * 16) + this.decode_pos];
        var b2 = this.mem[(this.decode_offset * 16) + this.decode_pos + 1];
        var operand2 = b2;
        var mod = ((0x10 & b1) >> 4);
        var operand1 = (0x07 & b1);
        var instruction = ((0xE0 & b1) >> 5);
        if(instruction == 0x07){ //hlt
            this.decoded_instructions[pos] = ["htl"];
            return false;
        }
        if(mod == 0){
            switch(instruction){
            case 0x00: //"jmp"
                this.decode_pos += 1;
                this.decoded_instructions[pos] = ["jmp", "r" + String(operand1)];
                return true;
            case 0x01: //"movr"
                this.decode_pos += 2;
                this.decoded_instructions[pos] =
                    this.swap_out_segment_registers(["movr", "r" + String(operand1), "r" +
                                                     String(operand2)]);
                return true;
            case 0x02: //"movm"
                this.decode_pos += 2;  
                this.decoded_instructions[pos] =
                    this.swap_out_segment_registers(["movm", "r" + String(operand1), "ds:r" +
                                                     String(operand2)]);
                return true;
            case 0x03: //"add"
                this.decode_pos += 2;
                this.decoded_instructions[pos] =
                    this.swap_out_segment_registers(["add", "r" + String(operand1), "r" +
                                                     String(operand2)]);
                return true;
            case 0x04: //"xor"
                this.decode_pos += 2;
                this.decoded_instructions[pos] =
                    this.swap_out_segment_registers(["xor", "r" + String(operand1), "r" +
                                                     String(operand2)]);
                return true;
            case 0x05: //"cmp"
                this.decode_pos += 2;
                this.decoded_instructions[pos] =
                    this.swap_out_segment_registers(["cmp", "r" + String(operand1), "r" +
                                                     String(operand2)]);
                return true;
            case 0x06: //"jmpe"
                this.decode_pos += 1;
                this.decoded_instructions[pos] = ["jmpe", "r" + String(operand1)];
                return true;
            }
            } else { //mod == 1
            switch(instruction){
            case 0x00: //"jmp"
                this.decode_pos += 2;
                this.decode_offset = operand2 * 16;
                this.decoded_instructions[pos] = ["jmp", "s" + String(operand2) + ":r" +
                                                  String(operand1)];
                return false;
            case 0x01: //"movr"
                this.decode_pos += 2;
                this.decoded_instructions[pos] =
                    this.swap_out_segment_registers(["movr", "r" + String(operand1), operand2]);
                return true;
            case 0x02: //"movm"
                this.decode_pos += 2;
                this.decoded_instructions[pos] =
                    this.swap_out_segment_registers(["movm", "ds:r" + String(operand1), "r" +
                                                     String(operand2)]);
                return true;
            case 0x03: //"add"
                this.decode_pos += 2;
                this.decoded_instructions[pos] =
                    this.swap_out_segment_registers(["add", "r" + String(operand1), operand2]);
                return true;
            case 0x04: //"xor"
                this.decode_pos += 2;
                this.decoded_instructions[pos] =
                    this.swap_out_segment_registers(["xor", "r" + String(operand1), operand2]);
                return true;
            case 0x05: //"cmp"
                this.decode_pos += 2;
                this.decoded_instructions[pos] =
                    this.swap_out_segment_registers(["cmp", "r" + String(operand1), operand2]);
                return true;
            case 0x06: //"jmpe"
                this.decode_pos += 2;
                this.decode_offset = operand2 * 16;
                this.decoded_instructions[pos] = ["jmpe", "s" + String(operand2) + ":r" +
                                                  String(operand1)];
                return false;
            }
        }
    },
    run_instructions: function(){
        document.write("<table border=\"1\">");
        var looping = true;
        while(looping && this.cpu.ip < 256){
            if(!(this.cpu.ip in this.decoded_instructions)){
                this.cpu.ip++;
                continue;
            }
            var instruction = this.decoded_instructions[this.cpu.ip];
            switch(instruction[0]){
            case "jmp":
                if(instruction[1][0] == "s"){
                    var re = /s(\d*):(r\d)/;
                    var vals = instruction[1].match(re);
                    this.cpu.cs = Number(vals[1]);
                    this.cpu.ip = eval("this.cpu." + vals[2]) - 1;
                    looping = false;
                } else {
                    this.cpu.ip = eval("this.cpu." + instruction[1]) - 1;
                }
                break;
            case "jmpe":
                if(this.cpu.fl != 0){
                    break;
                }
                if(instruction[1][0] == "s"){
                    var re = /s(\d*):(r\d)/;
                    var vals = instruction[1].match(re);
                    this.cpu.cs = Number(vals[1]);
                    this.cpu.ip = eval("this.cpu." + vals[2]) - 1;
                    looping = false;
                } else {
                    this.cpu.ip = eval("this.cpu." + instruction[1]) - 1;
                }
                break;
            case "movr":
                if(instruction[2][0] == "r" || instruction[2][0] == "c" ||
                        instruction[2][0] == "d"){
                    eval("this.cpu." + instruction[1] + " = this.cpu." + instruction[2])
                } else {
                    eval("this.cpu." + instruction[1] + " = " + instruction[2]);
                }
                break;
            case "movm":
                var re = /ds:(r\d*)/;
                if(instruction[1][0] == "d"){ //mod 1
                    var vals = instruction[1].match(re);
                    var r = eval("this.cpu." + vals[1]);
                    this.mem[r + (this.cpu.ds * 16)] = eval("this.cpu." + instruction[2]);
                } else {
                    var vals = instruction[2].match(re);
                    var r = eval("this.cpu." + vals[1]);
                    eval("this.cpu." + instruction[1] + " = this.mem[r + " +
                         (this.cpu.ds * 16) + "]");
                }
                break;
            case "add":
                if(instruction[2][0] == "r" || instruction[2][0] == "c" ||
                        instruction[2][0] == "d"){
                    eval("this.cpu." + instruction[1] + " += this.cpu." + instruction[2]);
                } else {
                    eval("this.cpu." + instruction[1] + " += " + instruction[2]);
                }  
                break;
            case "xor":
                if(instruction[2][0] == "r"){
                    eval("this.cpu." + instruction[1] + " ^= this.cpu." + instruction[2]);
                } else {
                    eval("this.cpu." + instruction[1] + " ^= " + instruction[2]);
                }  
                break;
            case "cmp":
                if(instruction[2][0] == "r"){
                    if(eval("this.cpu." + instruction[1]) == eval("this.cpu." + instruction[2])){
                 this.cpu.fl = 0;
                    } else if(eval("this.cpu." + instruction[1]) > eval("this.cpu." +
                                                                        instruction[2])){
                 this.cpu.fl = 1;
                    } else{
                 this.cpu.fl = 0xFF;
                    }
                } else {
                    if(eval("this.cpu." + instruction[1]) == instruction[2]){
                 this.cpu.fl = 0;
                    } else if(eval("this.cpu." + instruction[1]) > instruction[2]){
                 this.cpu.fl = 1;
                    } else{
                 this.cpu.fl = 0xFF;
                    }
                }
                break;
            case "hlt":
                looping = false;
                break;         
            }
            this.cpu.ip++;
            document.write("<tr>");
            document.write("<td>" + this.get_instruction_text(instruction));
            document.write("</td><td>" + this.get_state() + "</td></tr>");
        }
        document.write("</table></p>");
        this.decoded_instructions = {}
        this.decode_offset = this.cpu.cs;
        this.decode_pos = 0;
    },
    swap_out_segment_registers: function(instruction){
        for(var i = 0; i < instruction.length;i++){
            if(instruction[i] == "r4"){instruction[i] = "cs";}
            if(instruction[i] == "r5"){instruction[i] = "ds";}
        }
        return instruction;
   },
    print_mem: function(){
        document.write("Memory Dump<br />");
        for(var i = 0; i < this.mem.length; i++){
            if(this.mem[i] < 0x20 || this.mem[i] > 0x7E){
                var v = this.mem[i].toString(16);
                if(v.length == 1){
                    v = "0x0" + v;
                } else {
                    v = "0x" + v;
                }
                document.write(v + ", ");
            } else {
                document.write("'" + String.fromCharCode(this.mem[i]) + "' , ");
            }
            if(((i + 1) % 16) == 0){
                document.write("<br />");
            }
            if(((i + 1) % 256) == 0){
                document.write("<br />");
            }
        }
    },
    get_state: function(){
        var state = "ip = " + this.cpu.ip + ", ";
        state += "cs = " + this.cpu.cs + ", ";
        state += "ds = " + this.cpu.ds + ", ";
        state += "r0 = " + this.cpu.r0 + ", ";
        state += "r1 = " + this.cpu.r1 + ", ";
        state += "r2 = " + this.cpu.r2 + ", ";
        state += "r3 = " + this.cpu.r3 + ", ";
        state += "fl = " + this.cpu.fl + ".";
        return state;
    },
    get_instruction_text: function(instruction){
        var text = instruction[0];
        if(instruction.length == 2){
            text += " " + instruction[1];
        }
        if(instruction.length == 3){
            text += " " + instruction[1] + ", " + String(instruction[2]);
        }
        return text;
    },
    print_instructions: function(){
        document.write("<p>");
        document.write("Decoded instructions:<br />");
        document.write("<table border=\"1\">");
   
        for(var i = 0; i < 256 ; i++){
            if(!(i in this.decoded_instructions)){
                continue;
            }
            document.write("<tr>");
            var instruction = this.decoded_instructions[i];
            document.write("<td>At pos " + i + "</td>");
            document.write("<td>" + this.get_instruction_text(instruction));
            document.write("</td><td>");
        }
        document.write("</table></p>");
    },
    decode_pos : 0x00,
    decode_offset: 0x00,
    decode: function(){
        while(this.decode_mem()){ }
        this.print_instructions()
        this.run_instructions()
        this.print_mem()
        while(this.decode_mem()){ }
        this.print_instructions()
        this.run_instructions()
        this.print_mem()
/*
        this.decode_offset = 22;
        this.decode_pos = 0;
        while(this.decode_mem()){ }
        this.print_instructions()
        this.print_mem()
*/

    },
    instructions: ["jmp", "movr","movm", "add","xor","cmp","jmpe", "hlt"],
    exec: function()
    {
 // virtual machine architecture
 // ++++++++++++++++++++++++++++
 //
 // segmented memory model with 16-byte segment size (notation seg:offset)
 //
 // 4 general-purpose registers (r0-r3)
 // 2 segment registers (cs, ds equiv. to r4, r5)
 // 1 flags register (fl)
 //
 // instruction encoding
 // ++++++++++++++++++++
 //
 //           byte 1               byte 2 (optional)
 // bits      [ 7 6 5 4 3 2 1 0 ]  [ 7 6 5 4 3 2 1 0 ]
 // opcode      - - -            
 // mod               -          
 // operand1            - - - -
 // operand2                         - - - - - - - -
 //
 // operand1 is always a register index
 // operand2 is optional, depending upon the instruction set specified below
 // the value of mod alters the meaning of any operand2
 //   0: operand2 = reg ix
 //   1: operand2 = fixed immediate value or target segment (depending on instruction)
 //
 // instruction set
 // +++++++++++++++
 //
 // Notes:
 //   * r1, r2 => operand 1 is register 1, operand 2 is register 2
 //   * movr r1, r2 => move contents of register r2 into register r1
 //
 // opcode | instruction | operands (mod 0) | operands (mod 1)
 // -------+-------------+------------------+-----------------
 // 0x00   | jmp         | r1               | r2:r1
 // 0x01   | movr        | r1, r2           | rx,   imm
 // 0x02   | movm        | r1, [ds:r2]      | [ds:r1], r2
 // 0x03   | add         | r1, r2           | r1,   imm
 // 0x04   | xor         | r1, r2           | r1,   imm
 // 0x05   | cmp         | r1, r2           | r1,   imm
 // 0x06   | jmpe        | r1               | r2:r1
 // 0x07   | hlt         | N/A              | N/A
 //
 // flags
 // +++++
 //
 // cmp r1, r2 instruction results in:
 //   r1 == r2 => fl = 0
 //   r1 < r2  => fl = 0xff
 //   r1 > r2  => fl = 1
 //
 // jmpe r1
 //   => if (fl == 0) jmp r1
 //      else nop
 //    throw "VM.exec not yet implemented";
    }
 
};


//--------------------------------------------------------------------------------------------------


try
{
    VM.decode();
//    VM.exec();
}
catch(e)
{
    alert('\nError: ' + e + '\n');
}

//--------------------------------------------------------------------------------------------------
 

Use the following html file and load it in Chrome or Firefox to see it work:

<html>
    <head>
        <script type="text/javascript" src="stage2.js">
        </script>
    </head>
    <body>
    </body>
</html>


Now onto stage 3...


Thursday, 8 December 2011

Beating the GCHQ challenge (part 1)

GCHQ has issued a challenge to recruit new staff. The web page canyoucrackit.co.uk was launched with the following cryptic challenge.



A keyword is asked for by the site with very little else as a clue. It doesn't take much to see that this is x86 opcode - run it through ndisasm and you get this:

jmp short 0x6   ;EB04            
scasd    ;AF              
ret 0xa3bf   ;C2BFA3          
sub esp,0x100   ;81EC00010000    
xor ecx,ecx   ;31C9            
mov [esp+ecx],cl  ;880C0C          
inc cl    ;FEC1            
jnz 0xe    ;75F9            
xor eax,eax   ;31C0            
mov edx,0xdeadbeef  ;BAEFBEADDE      
add al,[esp+ecx]  ;02040C          
add al,dl   ;00D0            
ror edx,0x8   ;C1CA08          
mov bl,[esp+ecx]  ;8A1C0C          
mov bh,[esp+eax]  ;8A3C04          
mov [esp+eax],bl  ;881C04          
mov [esp+ecx],bh  ;883C0C          
inc cl    ;FEC1            
jnz 0x1c   ;75E8            
jmp dword 0x95   ;E95C000000      
mov ebx,esp   ;89E3            
add ebx,0x4   ;81C304000000    
pop esp    ;5C              
pop eax    ;58              
cmp eax,0x41414141  ;3D41414141      
jnz 0x8d   ;7543            
pop eax    ;58              
cmp eax,0x42424242  ;3D42424242      
jnz 0x8d   ;753B            
pop edx    ;5A              
mov ecx,edx   ;89D1            
mov esi,esp   ;89E6            
mov edi,ebx   ;89DF            
sub edi,ecx   ;29CF            
rep movsb   ;F3A4            
mov esi,ebx   ;89DE            
mov ecx,edx   ;89D1            
mov edi,ebx   ;89DF            
sub edi,ecx   ;29CF            
xor eax,eax   ;31C0            
xor ebx,ebx   ;31DB            
xor edx,edx   ;31D2            
inc al    ;FEC0            
add bl,[esi+eax]  ;021C06          
mov dl,[esi+eax]  ;8A1406          
mov dh,[esi+ebx]  ;8A341E          
mov [esi+eax],dh  ;883406          
mov [esi+ebx],dl  ;88141E          
add dl,dh   ;00F2            
xor dh,dh   ;30F6            
mov bl,[esi+edx]  ;8A1C16          
mov dl,[edi]   ;8A17            
xor dl,bl   ;30DA            
mov [edi],dl   ;8817            
inc edi    ;47              
dec ecx    ;49              
jnz 0x6b   ;75DE            
xor ebx,ebx   ;31DB            
mov eax,ebx   ;89D8            
inc al    ;FEC0            
int 0x80   ;CD80            
nop    ;90              
nop    ;90              
call dword 0x39   ;E89DFFFFFF      
inc ecx    ;41              
inc ecx    ;41              
inc ecx    ;41              
inc ecx    ;41              
 

All rather promising, but there's a few things to note:
  • The first instruction jumps over the next two. These jumped instructions look pretty suspicious so perhaps they aren't code. If they are data, then this can be recoded as the DWORD 0xa3bfc2af. This isn't relevant now though ;)
  • Almost at the end of the code there's a relative 'call', but no matching 'ret' (at least in the reachable code). The following instructions seem like nonsense, so it seems likely that the code uses an old trick to embed data within code. The 'call' puts the address of the following instruction on the stack, then branches the execution. The code does later check that a value popped from the stack (0x41414141) so it seems that everything after the call is in fact data, not code. Later, it also checks this area of memory for the value 0x42424242 but that is nowhere to be seen! 
It was strange that the code was presented as an image, not as text, or as a binary. This might just be as a way to annoy you in having to type the information before using it, but I guessed a little stenanography was involved. Opening up the file in a hex editor gives us this:


Notice the iTXt section? A quick look at the libpng site shows that this section should hold text, not the garbled junk that's actually there. The text looks hard to recognise, but the two '=' signs at the end is a dead give away for Base64 encoding. Sure enough, a little ipython work and the output was saved to a file. Running it through ndisasm gives a load of junk so its not more code, but the first four bytes are the ascii character 'B' or 42 in hex! That means that this is what's missing from the code - maybe not actual code but embedded data.

Adding this to the code (and fixing the relative offsets) gives us:
[BITS 32]
section .text
    global main

main:
    call code_fragment
    mov eax, 1
    xor ebx, ebx
    int 0x80

code_fragment:
    jmp short setup_stack ;EB04            
    scasd   ;AF              
    ret 0xa3bf  ;C2BFA3
setup_stack:
    sub esp,0x100  ;81EC00010000    
    xor ecx,ecx  ;31C9            
next_byte:
    mov [esp+ecx],cl ;880C0C      
    inc cl   ;FEC1            
    jnz next_byte  ;75F9            
    xor eax,eax  ;31C0            
    mov edx,0xdeadbeef ;BAEFBEADDE      
do_stuff:
    add al,[esp+ecx] ;02040C          
    add al,dl  ;00D0            
    ror edx,0x8  ;C1CA08          
    mov bl,[esp+ecx] ;8A1C0C          
    mov bh,[esp+eax] ;8A3C04          
    mov [esp+eax],bl ;881C04          
    mov [esp+ecx],bh ;883C0C          
    inc cl   ;FEC1            
    jnz do_stuff  ;75E8            
    jmp nop_start  ;E95C000000      
back_in:
    mov ebx,esp  ;89E3            
    add ebx,0x4  ;81C304000000    
    pop esp   ;5C              
    pop eax   ;58              
    cmp eax,0x41414141 ;3D41414141      
    jnz bail_out  ;7543            
    pop eax   ;58              
    cmp eax,0x42424242 ;3D42424242      
    jnz bail_out  ;753B            
    pop edx   ;5A              
    mov ecx,edx  ;89D1            
    mov esi,esp  ;89E6            
    mov edi,ebx  ;89DF            
    sub edi,ecx  ;29CF            
    rep movsb  ;F3A4            
    mov esi,ebx  ;89DE            
    mov ecx,edx  ;89D1            
    mov edi,ebx  ;89DF            
    sub edi,ecx  ;29CF            
    xor eax,eax  ;31C0            
    xor ebx,ebx  ;31DB            
    xor edx,edx  ;31D2            
go_again:
    inc al   ;FEC0            
    add bl,[esi+eax] ;021C06          
    mov dl,[esi+eax] ;8A1406          
    mov dh,[esi+ebx] ;8A341E          
    mov [esi+eax],dh ;883406          
    mov [esi+ebx],dl ;88141E          
    add dl,dh  ;00F2            
    xor dh,dh  ;30F6            
    mov bl,[esi+edx] ;8A1C16          
    mov dl,[edi]  ;8A17            
    xor dl,bl  ;30DA            
    mov [edi],dl  ;8817            
    inc edi   ;47              
    dec ecx   ;49              
    jnz go_again  ;75DE            
bail_out:
    xor ebx,ebx  ;31DB            
    mov eax,ebx  ;89D8            
    inc al   ;FEC0            
    int 0x80  ;CD80
nop_start:
    nop   ;90              
    nop   ;90              
    call back_in  ;E89DFFFFFF      
    inc ecx   ;41              
    inc ecx   ;41              
    inc ecx   ;41              
    inc ecx   ;41        
decoded_code:
    db 0x42
    db 0x42
    db 0x42
    db 0x42    
    db 0x32
    db 0x00
    db 0x00
    db 0x00  
    db 0x91
    db 0xd8
    db 0xf1
    db 0x6d
    db 0x70
    db 0x20
    db 0x3a
    db 0xab
    db 0x67
    db 0x9a
    db 0x0b
    db 0xc4
    db 0x91
    db 0xfb
    db 0xc7
    db 0x66
    db 0x0f
    db 0xfc
    db 0xcd
    db 0xcc
    db 0xb4
    db 0x02
    db 0xfa
    db 0xd7
    db 0x77
    db 0xb4
    db 0x54
    db 0x38
    db 0xab
    db 0x1f
    db 0x0e
    db 0xe3
    db 0x8e
    db 0xd3
    db 0x0d
    db 0xeb
    db 0x99
    db 0xc3
    db 0x93
    db 0xfe
    db 0xd1
    db 0x2b
    db 0x1b
    db 0x11
    db 0xc6
    db 0x11
    db 0xef
    db 0xc8
    db 0xca
    db 0x2f
 


Forgive the silly code labels - I wasn't going for production code here! Running the resulting binary through a debugger shows that the code copies the data after 0x42424242 and does a lot of bit fiddling with it. Follow the code and watch the memory it's messing with and you will end up with:


Now at this point I was expecting the whole solution of the challenge, so a http GET request was not very rewarding! Nevertheless, the resulting javascript file was now available and it was time to move onto stage 2.