Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > General > Decrypting

Reply
 
Thread Tools Search this Thread Display Modes
Old 30th August 2008, 11:59   #21  |  Link
Oopho2ei
Guest
 
Posts: n/a
I have added some more commands. It is becoming increasingly difficult to find good names eg. BEQ (for 'below or equal' inintel terms) is usually used for 'branch if equal'. If you can think of better names/abbreviations you are also welcome to post your ideas.
  Reply With Quote
Old 31st August 2008, 02:39   #22  |  Link
Oopho2ei
Guest
 
Posts: n/a
I can see that things are getting more complicated and my C-like expressions become quite ugly. A lot of people are following this thread but no one dares to say something. So i decided to give a very small example of how this works.

You have probably seen the BDSVM folder on your blue rays with BD+ protection. Now open the 00000.svm in a hex editor (like khexedit) and scroll down to address around 0x1000. You are looking now at some of the instructions which are executed by the virtual machine at the very beginning.

Code:
...
0000:1000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:1010 00 00 00 00 00 00 00 00 b0 00 00 40 e0 3d 00 08
0000:1020 e3 fd 00 04 d4 20 00 00 e0 1d 00 00 d7 e0 11 54
0000:1030 e3 e0 11 50 b4 01 9b 0c d7 e0 11 58 e3 e0 11 50
0000:1040 e0 20 00 04 d7 fd 00 04 d4 3d 00 08 67 bd 00 1c
0000:1050 e4 00 00 10 b3 ff ff fc 00 00 00 00 bc 00 ff fc
0000:1060 bc 01 ff fc bc 02 ff fc bc 03 ff fc bc 04 ff fc
0000:1070 bc 05 ff fc bc 06 ff fc bc 07 ff fc bc 08 ff fc
0000:1080 bc 09 ff fc bc 0a ff fc bc 0b ff fc bc 0c ff fc
0000:1090 bc 0d ff fc bc 0e ff fc bc 0f ff fc bc 10 ff fc
0000:10a0 bc 11 ff fc bc 12 ff fc bc 13 ff fc bc 14 ff fc
0000:10b0 bc 15 ff fc bc 16 ff fc bc 17 ff fc bc 18 ff fc
0000:10c0 bc 19 ff fc bc 1a ff fc bc 1b ff fc bc 1c ff fc
0000:10d0 bc 1d ff fc bc 1e ff fc bc 1f ff fc c3 a0 00 00
0000:10e0 67 bd 11 5c c3 80 00 00 67 9c 10 00 d7 bd 00 00
...
Now let's see how this works:

1. Fetch: Execution starts with PC = 0x1000. Because we have a 0x18 byte long header this translates to the start address 0x1000+0x18=0x1018. If we look at the section from 00000.svm posted above we see our first instruction: "b0 00 00 40".

2. Decode: If we look at the highest 6 bits of the leftmost byte of this instruction. The binary represenation of 0xB0 = 0b10110000. The highest 6 bits are 0b101100 which is 0x2C. (Instead of converting it binary representation and back we could have simply divided by 4: 0xB0/4 = 0x2C.) Now if we lookup 0x2C in the table i posted above you will see that this is the encoding of the command "JMP C".

3. Execute: Now lets look at what this instruction does. The corresponding entry of the table says: "C = PC + signed(unsigned(I) << 6) >> 6; PC = C". So we first calculate a constant C = PC + signed(unsigned(I) << 6) >> 6. What the second part does is simply setting the highest 6 bits of our instruction I = 0xB0000040 to the value of the 7th highest bit. This is done here by first doing a logic shift left and then an arithmetic shift right. (The expressions "signed" and "unsigned" are used to distinguish between logic and arithmetic shift because C handles the shift operator differently of signed and unsigned values.) Now in our case the 7th highest bit is 0 so we can just overwrite the highest 6 bits with zeros. The result is C = PC + 0x40.
Now when the command dispatcher runs it already increments the value of the program counter (pc) by 4 (4 bytes is the length of every instruction) so that it is pointing at the next instruction even before the command handler (in this case for "jmp c") is executed. So in this case our program counter is PC = 0x1000 + 4 = 0x1004. Now according to the description "PC = PC + signed(unsigned(I) << 6) >> 6" our new program counter is PC = 0x1004 + 0x40 = 0x1044.

4. Write back: PC = 0x1044

Now lets look at the next instruction.

1. Fetch: At 0x1044+0x18 we have "bc 00 ff fc"

2. Decode: 0xBC / 4 = 2F so we have a "JNZ R[X],C". Looking in the instruction table we find the description "C = PC + sign_extend(I & 0xFFFF); if (R[Y] != 0) PC = C".

3. We first calculate C. Therefor we overwrite the 16 highest bits with the value of the 17th highest bit (this is what this expression simply does). The 17th highest bit is 1 so we result is C = PC + 0xFFFFFFFC = 0x1044. Now we see that R[Y] is checked for being different from zero. According to the description Y = ((I >> 0x0E) & 0x7C) >> 2. This expression reads the 5 bits directly following the command number (highest 6 bits) from the instruction. In our case this is Y = 0 so we have to check register 0 (R[0]) for being zero. In this case it is really zero because the virtual machine has just been started and we haven't manipulated anything except the program counter so far. Consequently "PC = C" is not being executed and we can forget C and proceed with the next instruction.

4. Write back: nothing....

Execution proceeds at 0x1048 ...

You will find more details scattered across the postings of this thread. Feel free to ask if you have questions. Explaining things to other people really helps finding bugs.

Last edited by Oopho2ei; 31st August 2008 at 11:40.
  Reply With Quote
Old 31st August 2008, 13:05   #23  |  Link
bmnot
Registered User
 
Join Date: Jun 2007
Posts: 215
Experimental BDVM

Hi Oopho2ei,

I've been following the threads dealing with HDDVD/BluRay-Decryption on doom9 since the beginning and I must say you're doing an truly amazing job – despite the limited interest and resonance lately...
As probably many other members, I really appreciate your work on this topic (one reason is that there's no AnyDVD HD for my preferred operating systems...).

Regarding the topic...
As already stated somewhere in the SlySoft forums, the BD+ virtual machine (obviously) has not much in common with Java (no stack operands, no objects). It looks more like some simple MIPS dialect.

Therefore - up to this point - it's pretty straightforward to implement an interpreter. Actually, you already more or less posted the source code...

It appears, there are at least three types of instructions (just another notation of what was already posted):

Type1 [6bit command][5bit Operand X][5bit Operand Y][5bit Operand Z][11 bit unused]
Type2 [6bit command][5bit Operand X][5bit Operand Y][16bit constant] (5bit constant in shift operations)
Type3 [6bit command][26bit constant]

Regarding the functionality of commands 36-3A, there are still operations missing to store values in memory, to perform systems calls and to execute native code.

It would be interesting to know how the VM deals with arithmetic overflows (ADD, MUL)... or if they are simply not allowed in BD+.

Is the PC counted in bytes or words? In the below implementation it's bytes (as in your tutorial). However, this would unnecessarily restrict the range of jump commands.

Below is a simple, experimental C++ implementaion of the BD+ VM based on your specs (compiles, but not tested). Targets were keeping it short, maintainable and close to your notation. Hope I got all the signs right... Not sure about the commands for loading values from memory...

Code:
/* this code is public domain */

// the following lines could be moved to a separate header file later
typedef unsigned char byte; // for less typing
typedef unsigned int uint;  // dito

class ExperimentalBDVM {
public:
    void runProgram(byte* pcBaseAddress, uint pcStartOffset = 0x1000);
};
// end header

const int MEMSIZE = 0x400000; // 4MB address space

uint makeint(byte b3, byte b2, byte b1, byte b0) {
    return ((int)b3) << 24 | ((int)b3) << 16 | ((int)b2) << 8 | ((int)b0);
}

int _signed(uint x)   { return (int)x; }
int _signed(int x)    { return x; }
int _unsigned(uint x) { return x; }
int _unsigned(int x)  { return (uint)x; }

void ExperimentalBDVM::runProgram(byte* pcBaseAddress, uint pcStartOffset) {

    // init VM
    uint PC = pcStartOffset; // program counter
    uint R[32]; // registers
    byte mem[MEMSIZE]; // memory

    // init memory
    for (int i = 0; i < MEMSIZE; i++) {
        mem[i] = 0;
    }

    // temporary variables
    byte* addr;
    int b = 0;

    // process commands in (currently infinite) lo
    while(true) {

        // instruction fetch
        byte* pca = pcBaseAddress + PC; // PC memory address
        uint I = makeint(pca[3], pca[2], pca[1], pca[0]); // instruction
        uint C = I >> 0x1A; // command
        uint X = ((I >> 0x13) & 0x7C) >> 2; // operand X
        uint Y = ((I >> 0x0E) & 0x7C) >> 2; // operand Y
        uint Z = ((I >> 0x09) & 0x7C) >> 2; // operand Z
        uint C16 = I & 0xFFFF; // 16 bit constant
        int C16S = _signed(_unsigned(I) << 16) >> 16; // same signed
        uint C5 = I & 0x1F; // 5 bit constant
        //uint C26 = I & 0x3FFFFFF; // 26 bit constant
        int C26S = _signed(_unsigned(I) << 6) >> 6; // same signed
        PC += 4; // already increase PC

        // execute instruction
        switch (C) {
        case 0x01: R[X] = R[Y] + R[Z]; break; // ADD
        case 0x02: R[X] = R[Y] - R[Z]; break; // SUB
        case 0x03: R[X] = R[Y] * R[Z]; break; // MUL
        case 0x04: R[X] = _unsigned(R[Y]) / R[Z]; break; // IDIV
        case 0x05: R[X] = _signed(R[Y]) / R[Z]; // DIV
        case 0x06: R[X] = R[Y] << (R[Z] & 0x1F); break; // SHL
        case 0x07: R[X] = R[Y] >> (R[Z] & 0x1F); break; // SHR
        case 0x08: R[X] = _signed(R[Y]) >> (R[Z] & 0x1F); break; // SAR
        case 0x09: R[X] = R[Y] & R[Z]; break; // AND
        case 0x0A: R[X] = R[Y] | R[Z]; break; // OR
        case 0x0B: R[X] = R[Y] ^ R[Z]; break; // XOR
        case 0x0C: R[X] = (R[Y] == R[Z]) ? 1 : 0; break; // EQU
        case 0x0D: R[X] = (R[Y] != R[Z]) ? 1 : 0; break; // NEQU
        case 0x0E: R[X] = (_signed(R[Y]) < _signed(R[Z])) ? 1 : 0; break; // LESS
        case 0x0F: R[X] = (_unsigned(R[Y]) < _unsigned(R[Z])) ? 1 : 0; break; // BLW
        case 0x10: R[X] = (_signed(R[Y]) > _signed(R[Z])) ? 1 : 0; break; // GRE
        case 0x11: R[X] = (_unsigned(R[Y]) > _unsigned(R[Z])) ? 1 : 0; break; // ABV
        case 0x12: R[X] = (_signed(R[Y]) <= _signed(R[Z])) ? 1 : 0; break; // LEQ
        case 0x13: R[X] = (_unsigned(R[Y]) <= _unsigned(R[Z])) ? 1 : 0; break; // BEQ
        case 0x14: R[X] = (_signed(R[Y]) >= _signed(R[Z])) ? 1 : 0; break; // GEQ
        case 0x15: R[X] = (_unsigned(R[Y]) >= _unsigned(R[Z])) ? 1 : 0; break; // AEQ
        case 0x16: PC = R[Y]; break; // JMP
        case 0x17: R[0x1F] = PC; PC = R[Y]; break; // CALL
        // same with constants
        case 0x18: R[X] = R[Y] + C16S; break; // ADDE
        case 0x19: R[X] = R[Y] + C16; break; // ADD
        case 0x1A: R[X] = R[Y] - C16S; break;// SUBE
        case 0x1B: R[X] = R[Y] - C16; break; // SUB
        case 0x1C: R[X] = _unsigned(R[Y]) << C5; break; // SHL
        case 0x1D: R[X] = _unsigned(R[Y]) >> C5; break; // SHR
        case 0x1E: R[X] = _signed(R[Y]) >> C5; break; // SAR
        case 0x1F: R[X] = R[Y] & C16; break; // AND
        case 0x20: R[X] = R[Y] | C16; break; // OR
        case 0x21: R[X] = R[Y] ^ C16; break; // XOR
        case 0x22: R[X] = (_signed(R[Y]) == C16S) ? 1 : 0; break; // EQU
        case 0x23: R[X] = (_signed(R[Y]) != C16S) ? 1 : 0; break; // NEQU
        case 0x24: R[X] = (_signed(C16S) > _signed(R[Y])) ? 1 : 0; break; // GRE
        case 0x25: R[X] = (_unsigned(C16) > _unsigned(R[Y])) ? 1 : 0; break; // ABV
        case 0x26: R[X] = (_signed(C16S) < _signed(R[Y])) ? 1 : 0; break; // LESS
        case 0x27: R[X] = (_unsigned(C16) < _unsigned(R[Y])) ? 1 : 0; break; // BLW
        case 0x28: R[X] = (_signed(C16S) >= _signed(R[Y])) ? 1 : 0; break; // GEQ
        case 0x29: R[X] = (_unsigned(C16) >= _unsigned(R[Y])) ? 1 : 0; break; // AEQ
        case 0x2A: R[X] = (_signed(C16S) <= _signed(R[Y])) ? 1 : 0; break; // LEQ
        case 0x2B: R[X] = (_unsigned(C16) <= _unsigned(R[Y])) ? 1 : 0; break; // BEQ
        case 0x2C: PC += C26S; break; // JMP
        case 0x2D: R[0x1F] = PC; PC += C26S; break; // CALL

        case 0x2E: if (R[Y] == 0) PC += C16S; break; // JZ
        case 0x2F: if (R[Y] != 0) PC += C16S; break; // JNZ
        case 0x30: R[X] = I; break; // STR (correct??)
        case 0x31: // LBSE
        case 0x32: // LB
            // correct?? (the ^ 0x03 appears strange...)
            addr = &mem[((R[Y] + C16S) & 0x3FFFFF) ^ 0x03];
            b = addr[0];
            if (C == 0x31 && b >= 0x80) { // process sign
                b-= 0x100;
            }
            R[X] = b;
            break;
        case 0x33: // LWSE
        case 0x34: // LW
            // correct??
            addr = &mem[((R[Y] + C16S) & 0x3FFFFE) ^ 0x02];
            b = (int)makeint(0, 0, addr[1], addr[0]);
            if (C == 0x33 && b >= 0x8000) { // process sign
                b -= 0x10000;
            }
            R[X] = b;
            break;
        case 0x35: // LDW
            addr = &mem[(R[Y] + C16S) & 0x3FFFFC];
            R[X] = makeint(addr[3], addr[2], addr[1], addr[0]);
            break;
        default: break; // NOP or unknown
        }
    }
}

int main() {
    // TODO
}
bmnot is offline   Reply With Quote
Old 31st August 2008, 15:04   #24  |  Link
Oopho2ei
Guest
 
Posts: n/a
Quote:
Originally Posted by bmnot View Post
Regarding the functionality of commands 36-3A, there are still operations missing to store values in memory, to perform systems calls and to execute native code.
I have updated commands 0x31-0x35 (improved syntax) and added the remaining commands 0x36-0x3A.

Quote:
Originally Posted by bmnot View Post
It would be interesting to know how the VM deals with arithmetic overflows (ADD, MUL)... or if they are simply not allowed in BD+.
Exceptions are handled by the interpreter but i haven't documented them yet.

Quote:
Originally Posted by bmnot View Post
Is the PC counted in bytes or words? In the below implementation it's bytes (as in your tutorial). However, this would unnecessarily restrict the range of jump commands.
Actually the PC is double word (32 bit) aligned. Every instruction is 32 bit long anyway. In my notes the PC is 4 bytes but the lower and higher bits are masked (PC & 0x3FFFFC). Have a look at what the dispatcher does:
Code:
previous instruction handler returns (has maybe changed PC)
...
command dispatcher is called: 
...
PC = PC AND 0x3FFFFC; // make it dword aligned
I = mem[PC] ^ instruction_filter_value;
PC = PC + 4;
...
command_handler(I >> 0x1A);
....
command handler returns and dispatcher is called again. and so on...
Ignore the "instruction_filter_value". It's set by command ??? and is zero in my case.

Quote:
Originally Posted by bmnot View Post
Below is a simple, experimental C++ implementaion of the BD+ VM based on your specs (compiles, but not tested). Targets were keeping it short, maintainable and close to your notation. Hope I got all the signs right... Not sure about the commands for loading values from memory...
Great!
But you should probably use your own notation and keep my weird looking/confusing notations as comments.

I will record a trace of the first few commands executed by the virtual machine. Like
Code:
0000:1000 b0 00 00 40 -> PC = 0x1044
0000:1044 bc 00 ff fc -> no change
0000:1048 ...
We will use this for debugging. The 00000.svm is here: http://uploaded.to/?id=jqstc8

Edit: please use "cmd" for command and not "C" because i use "C" already otherwise. thanks.

Last edited by Oopho2ei; 2nd September 2008 at 22:08. Reason: 0x3FFFFFFC should have been 0x3FFFFC
  Reply With Quote
Old 31st August 2008, 17:03   #25  |  Link
Oopho2ei
Guest
 
Posts: n/a
Here is a short trace of the beginning of the VM execution. It should be enough to get the machine started.
Code:
PC = 0x00001000
WD = 0x7FFFFFFF

0000:1000 B0000040 WD=0x7FFFFFFD; PC=0x00001044; 
0000:1044 BC00FFFC WD=0x7FFFFFFB;
0000:1048 BC01FFFC WD=0x7FFFFFF9;
0000:104C BC02FFFC WD=0x7FFFFFF7;
0000:1050 BC03FFFC WD=0x7FFFFFF5;
0000:1054 BC04FFFC WD=0x7FFFFFF3;
0000:1058 BC05FFFC WD=0x7FFFFFF1;
0000:105C BC06FFFC WD=0x7FFFFFEF;
0000:1060 BC07FFFC WD=0x7FFFFFED;
0000:1064 BC08FFFC WD=0x7FFFFFEB;
0000:1068 BC09FFFC WD=0x7FFFFFE9;
0000:106C BC0AFFFC WD=0x7FFFFFE7;
0000:1070 BC0BFFFC WD=0x7FFFFFE5;
0000:1074 BC0CFFFC WD=0x7FFFFFE3;
0000:1078 BC0DFFFC WD=0x7FFFFFE1;
0000:107C BC0EFFFC WD=0x7FFFFFDF;
0000:1080 BC0FFFFC WD=0x7FFFFFDD;
0000:1084 BC10FFFC WD=0x7FFFFFDB;
0000:1088 BC11FFFC WD=0x7FFFFFD9;
0000:108C BC12FFFC WD=0x7FFFFFD7;
0000:1090 BC13FFFC WD=0x7FFFFFD5;
0000:1094 BC14FFFC WD=0x7FFFFFD3;
0000:1098 BC15FFFC WD=0x7FFFFFD1;
0000:109C BC16FFFC WD=0x7FFFFFCF;
0000:10A0 BC17FFFC WD=0x7FFFFFCD;
0000:10A4 BC18FFFC WD=0x7FFFFFCB;
0000:10A8 BC19FFFC WD=0x7FFFFFC9;
0000:10AC BC1AFFFC WD=0x7FFFFFC7;
0000:10B0 BC1BFFFC WD=0x7FFFFFC5;
0000:10B4 BC1CFFFC WD=0x7FFFFFC3;
0000:10B8 BC1DFFFC WD=0x7FFFFFC1;
0000:10BC BC1EFFFC WD=0x7FFFFFBF;
0000:10C0 BC1FFFFC WD=0x7FFFFFBD;
0000:10C4 C3A00000 WD=0x7FFFFFBC; R[0x1F]=0x00000000;
0000:10C8 67BD115C WD=0x7FFFFFBB; R[0x1D]=0x0000115C;
0000:10CC C3800000 WD=0x7FFFFFBA; R[0x1C]=0x00000000;
0000:10D0 679C1000 WD=0x7FFFFFB9; R[0x1C]=0x00001000;
0000:10D4 D7BD0000 WD=0x7FFFFFB7; R[0x1D]=0x6FBD001C;
0000:10D8 E3BC0000 WD=0x7FFFFFB5; mem[0x001000]=0x6FBD001C; 
0000:10DC D7A10000 WD=0x7FFFFFB3; R[0x1D]=0x00000000;
0000:10E0 C3A0001F WD=0x7FFFFFB2; R[0x1D]=0x001F0000;
0000:10E4 67BDFFFC WD=0x7FFFFFB1; R[0x1D]=0x001FFFFC;
0000:10E8 B4000008 WD=0x7FFFFFAE; PC=0x000010F4; R[0x1F]=0x000010EC; 
0000:10F4 D43F0000 WD=0x7FFFFFAC; R[0x01]=0x00000000;
0000:10F8 B8010018 WD=0x7FFFFFAA; PC=0x00001114;
0000:1114 6FBDFFFC WD=0x7FFFFFA9; R[0x1D]=0x001F0000;
0000:1118 C3800000 WD=0x7FFFFFA8; R[0x1C]=0x00000000;
0000:111C 679C1160 WD=0x7FFFFFA7; R[0x1C]=0x00001160;
0000:1120 C0200000 WD=0x7FFFFFA6; R[0x01]=0x00000000; 
0000:1124 64211040 WD=0x7FFFFFA5; R[0x01]=0x00001040;
0000:1128 D4210000 WD=0x7FFFFFA3; R[0x01]=0x00000000;
0000:112C 6FBD0004 WD=0x7FFFFFA2; R[0x1D]=0x001EFFFC; 
0000:1130 E03D0000 WD=0x7FFFFFA0; mem[0x1EFFFC]=0x00000000;
0000:1134 B4000004 WD=0x7FFFFF9D; PC=0x0000113C; R[0x1F]=0x00001138;
The watchdog (WD) is decremented by the corresponding amount of cycles after the execution of every instruction according to the table below. As long as the watchdog is greater than zero execution continues. Otherwise it breaks.
Edit: the table is:
Code:
0x01,0x01,0x01,0x04,0x10,0x10,0x01,0x01, // cmd00-cmd07
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, // cmd07-cmd0F
0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x03, // cmd10-cmd17
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, // cmd18-cmd1F
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, // cmd20-cmd27
0x01,0x01,0x01,0x01,0x02,0x03,0x02,0x02, // cmd28-cmd2F
0x01,0x04,0x04,0x04,0x04,0x02,0x04,0x04, // cmd30-cmd37
0x02,0x00,0x06,0x01,0x01,0x01,0x01,0x01  // cmd38-cmd3F
Cmd39 resets the timer back to 7FFFFFFFh. Btw the command 0x30 was wrong. I forgot a shift. It's fixed now.

Last edited by Oopho2ei; 6th September 2008 at 13:01.
  Reply With Quote
Old 31st August 2008, 18:07   #26  |  Link
ala42
Registered User
 
ala42's Avatar
 
Join Date: Aug 2008
Posts: 19
It might make sense to use the DLX mnemonics, like at http://www.csee.umbc.edu/courses/und...ing96/dlx.html and http://www.ee.byu.edu/class/ee428/labs/DLXinst.html.
ala42 is offline   Reply With Quote
Old 31st August 2008, 20:08   #27  |  Link
Oopho2ei
Guest
 
Posts: n/a
Quote:
Originally Posted by ala42 View Post
hi!

Thanks for the hint. I have started the mapping. It is much easier to read now.

Instruction Formats:
Code:
---------------------------------------------------------------------------------
|Format |                                 Bits                                  |
|--------------------------------------------------------------------------------
|       | 31 ... 26 | 25 ... 21 | 20 ... 16 | 15 ... 11 | 10 ... 06 | 05 ... 00 |
|--------------------------------------------------------------------------------
|R-type |   opcode  |    Rd     |    Rs1    |    Rs2    |    ???    |    ???    |
|I-type |   opcode  |    Rd     |    Rs1    | <---------- immediate --------->  |
|J-type |   opcode  |  <-------------------  immediate  --------------------->  |
|--------------------------------------------------------------------------------
Instructions:
Code:
Opcode | Instr | Description                               | Format | Operation (C-style coding)
-------------------------------------------------------------------------------------------------
 0x00  | NOP   | do nothing                                |        |
 0x01  | ADD   | add                                       | R-type | Rd = Rs1 + Rs2
 0x02  | SUB   | substract                                 | R-type | Rd = Rs1 - Rs2
 0x03  | MUL   | multipliply                               | R-type | Rd = Rs1 * Rs2
 0x04  | DIV   | signed integer division                   | R-type | Rd = signed(Rs1) / signed(Rs2)
 0x05  | DIVU  | unsigned integer division                 | R-type | Rd = unsigned(Rs1) / unsigned(Rs2)
 0x06  | SLL   | shift left logical                        | R-type | Rd = Rs1 << (Rs2 & 0x1F)
 0x07  | SRL   | shift right logical                       | R-type | Rd = Rs1 >> (Rs2 & 0x1F)
 0x08  | SRA   | shift right arithmetic                    | R-type | as SRL & see notes below
 0x09  | AND   | and                                       | R-type | Rd = Rs1 & Rs2
 0x0A  | OR    | or                                        | R-type | Rd = Rs1 | Rs2
 0x0B  | XOR   | exclusive or                              | R-type | Rd = Rs1 ^ Rs2
 0x0C  | SEQ   | set if equal                              | R-type | Rd = (Rs1 == Rs2 ? 1 : 0)
 0x0D  | SNE   | set if not equal                          | R-type | Rd = (Rs1 != Rs2 ? 1 : 0)
 0x0E  | SLT   | set if less than                          | R-type | Rd = (signed(Rs1) < signed(Rs2) ? 1 : 0)
 0x0F  | SBT   | set if below than                         | R-type | Rd = (unsigned(Rs1) < unsigned(Rs2) ? 1 : 0)
 0x10  | SGT   | set if greater than                       | R-type | Rd = (signed(Rs1) > signed(Rs2) ? 1 : 0)
 0x11  | SAT   | set if above than                         | R-type | Rd = (unsigned(Rs1) > unsigned(Rs2) ? 1 : 0)
 0x12  | SLE   | set if less than or equal                 | R-type | Rd = (signed(Rs1) <= signed(Rs2) ? 1 : 0)
 0x13  | SBE   | set if below or equal than                | R-type | Rd = (unsigned(Rs1) <= unsigned(Rs2) ? 1 : 0)
 0x14  | SGE   | set if greater than or equal              | R-type | Rd = (signed(Rs1) >= signed(Rs2) ? 1 : 0)
 0x15  | SAE   | set if above or equal than                | R-type | Rd = (unsigned(Rs1) >= unsigned(Rs2) ? 1 : 0)
 0x16  | JR    | jump register                             | I-type | PC = Rs1
 0x17  | JALR  | jump and link register                    | I-type | R31 = PC + 4 ; PC = Rs1
 0x18  | ADDIE | add immediate sign extended               | I-type | Rd = Rs1 + extend(immediate)
 0x19  | ADDI  | add immediate                             | I-type | Rd = Rs1 + immediate
 0x1A  | SUBIE | substract immediate sign extended         | I-type | Rd = Rs1 - extend(immediate)
 0x1B  | SUBI  | substract immediate                       | I-type | Rd = Rs1 - immediate
 0x1C  | SLLI  | shift left logical immediate              | I-type | Rd = Rs1 << (immediate & 0x1F)
 0x1D  | SRLI  | shift right logical immediate             | I-type | Rd = Rs1 >> (immediate & 0x1F)
 0x1E  | SRAI  | shift right arithmetic immediate          | I-type | as SRLI & see notes below
 0x1F  | ANDI  | and immediate                             | I-type | Rd = Rs1 & immediate
 0x20  | ORI   | or immediate                              | I-type | Rd = Rs1 | immediate
 0x21  | XORI  | exclusive or immediate                    | I-type | Rd = Rs1 ^ immediate
 0x22  | SEQI  | set if equal to immediate                 | I-type | Rd = (Rs1 == extend(immediate) ? 1 : 0)
 0x23  | SNEI  | set if not equal to immediate             | I-type | Rd = (Rs1 != extend(immediate) ? 1 : 0)
 0x24  | SLI   | set if less than immediate                | I-type | Rd = (signed(Rs1) < extend(immediate) ? 1 : 0)
 0x25  | SBI   | set if below than immediate               | I-type | Rd = (unsigned(Rs1) < unsigned(immediate) ? 1 : 0)
 0x26  | SGI   | set if greater than immediate             | I-type | Rd = (signed(Rs1) > extend(immediate) ? 1 : 0)
 0x27  | SAI   | set if above than immediate               | I-type | Rd = (unsigned(Rs1) > unsigned(immediate) ? 1 : 0)
 0x28  | SLEI  | set if less than or equal to immediate    | I-type | Rd = (signed(Rs1) <= extend(immediate) ? 1 : 0)
 0x29  | SBEI  | set if below than or equal to immediate   | I-type | Rd = (unsigned(Rs1) <= unsigned(immediate) ? 1 : 0)
 0x2A  | SGEI  | set if greater than or equal to immediate | I-type | Rd = (signed(Rs1) >= extend(immediate) ? 1 : 0)
 0x2B  | SAEI  | set if avove than or equal to immediate   | I-type | Rd = (unsigned(Rs1) >= unsigned(immediate) ? 1 : 0)
 0x2C  | J     | jump                                      | J-type | PC += extend(value)
 0x2D  | JAL   | jump and link                             | J-type | R31 = PC + 4 ; PC += extend(value)
 0x2E  | BEQZ  | branch if equal to zero                   | I-type | PC += (Rs1 == 0 ? extend(immediate) : 0)
 0x2F  | BNEZ  | branch if not equal to zero               | I-type | PC += (Rs1 != 0 ? extend(immediate) : 0)
 0x30  | LHI   | load high bits                            | I-type | Rd = immediate << 16
 0x31  | LBE   | load byte sign extended                   | I-type | Rd = extend(MEM[Rs1 + extend(immediate)] && 0xFF) (?) 
 0x32  | LB    | load byte                                 | I-type | Rd = MEM[Rs1 + extend(immediate)] && 0xFF (?)
 0x33  | LWE   | load word sign extended                   | I-type | Rd = extend(MEM[Rs1 + extend(immediate)] && 0xFFFF) (?)
 0x33  | LW    | load word                                 | I-type | Rd = MEM[Rs1 + extend(immediate)] && 0xFFFF (?)
 0x35  | LDW   | load double word                          | I-type | Rd = MEM[Rs1 + extend(immediate)]
 0x36  | SB    | store byte                                | I-type | MEM[Rs1 + extend(immediate)] = (unsigned char) Rd
 0x37  | SW    | store word                                | I-type | MEM[Rs1 + extend(immediate)] = (uint16) Rd
 0x38  | SDW   | store double word                         | I-type | MEM[Rs1 + extend(immediate)] = (uint32) Rd
 0x39  | TRAP  | call a trap function                      | J-type | trap[immediate]
 0x3A  | INSTF | set instruction filter value              | I-type | IF = Rs1
 0x3B  | NOP   | do nothing                                |        |
 0x3C  | NOP   | do nothing                                |        |
 0x3D  | NOP   | do nothing                                |        |
 0x3E  | NOP   | do nothing                                |        |
 0x3F  | NOP   | do nothing                                |        |
-------------------------------------------------------------------------------------------------
Notes:
- All immediate values are padded with zeros on the left unless indicated by extend(), in which case they are padded with copies of the left-most bit in the immediate value. Also, some instructions don't use all of the fields available for the format.
- SRA and SRAI are arithmetic right shifts. This means that, instead of shifting in zeroes from the left, the sign bit of the operand is duplicated. SRL and SRA perform identically if Rs1 is positive. If Rs1 is negative (bit 31 == 1), 1's are shifted in from the left for SRA and SRAI.

Quote:
Originally Posted by bmnot
// correct?? (the ^ 0x03 appears strange...)
addr = &mem[((R[Y] + C16S) & 0x3FFFFF) ^ 0x03];
The ^ 0x03 and ^ 0x02 in the address calculation is probably an adjustment for the reversed byte order of the dword so the byte or word is written at the correct position. Conversion between big endian and little endian does happen quite often inside the vm interpreter. Maybe we can just ignore this. Probably depends on the data structure we use for the memory.

Last edited by Oopho2ei; 14th September 2008 at 21:30. Reason: fixed cmd39
  Reply With Quote
Old 31st August 2008, 21:14   #28  |  Link
evdberg
Registered User
 
Join Date: Dec 2006
Posts: 202
Quote:
X = ((I >> 0x13) & 0x7C) >> 2
Y = ((I >> 0x0E) & 0x7C) >> 2
Z = ((I >> 0x09) & 0x7C) >> 2
Why not simply:
X = (I >> 21) & 31
Y = (I >> 16) & 31
Z = (I >> 11) & 31

Just returned from holiday and very pleased to see all your work! I assume you all got it from the patent description?
__________________
Developer DVD2one - http://www.dvd2one.com/
evdberg is offline   Reply With Quote
Old 31st August 2008, 21:53   #29  |  Link
Oopho2ei
Guest
 
Posts: n/a
Quote:
Originally Posted by evdberg View Post
Why not simply:
X = (I >> 21) & 31
Y = (I >> 16) & 31
Z = (I >> 11) & 31
It's equivalent. The >> 2 does the conversion from a byte address to the corresponding address in a dword (32 bit) aligned memory structure. In this case our Register array uint32 R[32]. This is really confusing sometimes. I hope the implementation does somebody else.
Quote:
Originally Posted by evdberg View Post
Just returned from holiday and very pleased to see all your work! I assume you all got it from the patent description?
Most of it is from compromised players. I won't give any details about which and how. Don't even try.

Last edited by Oopho2ei; 31st August 2008 at 22:09.
  Reply With Quote
Old 31st August 2008, 23:00   #30  |  Link
evdberg
Registered User
 
Join Date: Dec 2006
Posts: 202
Which is the TRAP instruction? According to the patent "one exemplary embodiment" (BD+?) would use the DLX "trap" instruction to provide access to external procedure calls (see item 127).
__________________
Developer DVD2one - http://www.dvd2one.com/
evdberg is offline   Reply With Quote
Old 1st September 2008, 07:55   #31  |  Link
Oopho2ei
Guest
 
Posts: n/a
Quote:
Originally Posted by evdberg View Post
Which is the TRAP instruction? According to the patent "one exemplary embodiment" (BD+?) would use the DLX "trap" instruction to provide access to external procedure calls (see item 127).
I don't know but cmd39 (called "INSTF" here) looks weird. It is also executed quite often although the instruction filter value is set to 0 all the time.
Anyway I am pretty sure i observed trap_xorblock(...) doing the decryption of section 2 in file 00001.vsm so i would only need to trace it back to an instruction.

A general note:
I am trying to make a top-down approach (to get an overview) adding more details with every sweep. The exception handling for the instructions is still missing too as you might have noticed. Furthermore the implementation needs to be close to 'perfect' because every mistake which distinguishes our interpreter from a licensed one can be targeted by future bd+ movie releases.

Last edited by Oopho2ei; 1st September 2008 at 08:27.
  Reply With Quote
Old 1st September 2008, 20:31   #32  |  Link
schluppo
Guest
 
Posts: n/a
nice work... i was thinking that maybe some kind of disasm for .svm-files would be appreciated.

so, which parts of the .svm-files are encrypted and is the decryption done by feeding the few unencrypted setup commands in the beginning of the file to the vm or is it always (i.e. for the complete file(s)) happening in the standard way that you mentioned?

----
(by standard way, i mean: )

Instruction I: C R[X], R[Y], R[Z]
C = I >> 0x1A
X = ((I >> 0x13) & 0x7C) >> 2
Y = ((I >> 0x0E) & 0x7C) >> 2
Z = ((I >> 0x09) & 0x7C) >> 2
----
  Reply With Quote
Old 1st September 2008, 21:07   #33  |  Link
Oopho2ei
Guest
 
Posts: n/a
Quote:
Originally Posted by schluppo View Post
nice work... i was thinking that maybe some kind of disasm for .svm-files would be appreciated.
Yes this would certainly be helpful.

Quote:
Originally Posted by schluppo View Post
so, which parts of the .svm-files are encrypted and is the decryption done by feeding the few unencrypted setup commands in the beginning of the file to the vm or is it always (i.e. for the complete file(s)) happening in the standard way that you mentioned?
It currently looks like the machine decrypts itself (at least part of it's data) with the help of traps. Furthermore there is a "instruction filter" which is a 32 bit value that is xored with the current 'obfuscated' instruction to decrypt it. This value can be updated dynamically and was introduced to make static code analysis impossible. But if your program can run 'on top' of our interpreter/emulator i don't see any problems. By running 'on top' i mean your disassembler can access usefull information of the emulator at any time and maybe store it for later use.

Quote:
Originally Posted by schluppo View Post
----
(by standard way, i mean: )

Instruction I: C R[X], R[Y], R[Z]
C = I >> 0x1A
X = ((I >> 0x13) & 0x7C) >> 2
Y = ((I >> 0x0E) & 0x7C) >> 2
Z = ((I >> 0x09) & 0x7C) >> 2
----
Please use the much clearer description of posting 27 of this thread (link: http://forum.doom9.org/showthread.ph...37#post1177737)

Last edited by Oopho2ei; 1st September 2008 at 21:25.
  Reply With Quote
Old 1st September 2008, 21:19   #34  |  Link
Oopho2ei
Guest
 
Posts: n/a
Quote:
Originally Posted by evdberg View Post
Which is the TRAP instruction? According to the patent "one exemplary embodiment" (BD+?) would use the DLX "trap" instruction to provide access to external procedure calls (see item 127).
Actually cmd39 is the trap instruction. I was simply fooled by the save and restore of the VM state (which includes the current instruction filter).

The trap id seems to be the immediate value. I found a jump/vector table in the vm code of my sample blue ray disk calling cmd39 with the following immediate values:
Code:
000010 = void TRAP_Finished();
000020
000110 = UINT32 TRAP_Aes(UINT8 *dst, UINT8 *src, UINT32 len, UINT8 *key, UINT32 opOrKeyID);
000120 = UINT32 TRAP_PrivateKey(UINT32 keyID, UINT8 *dst, UINT8 *src, UINT32 srcLen, UINT32 controlWord);
000130 = UINT32 TRAP_Random(UINT8 *dst, UINT32 len);
000140 = UINT32 TRAP_Sha(UINT8 *dst, UINT8 *src, UINT32 len, UINT32 op); 
000210 = UINT32 TRAP_AddWithCarry(UINT32 *dst, UINT32 *src, UINT32 len);
000220 = UINT32 TRAP_MultiplyWithRipple(UINT32 *dst, UINT32 *src, UINT32 len, UINT32 multiplicand);
000230 = UINT32 TRAP_XorBlock(UINT32 *dst, UINT32 *src, UINT32 len); 
000310 = UINT32 TRAP_Memmove(UINT8 *dst, UINT8 *src, UINT32 len);
000320 = UINT32 TRAP_MemSearch(UINT8 *Region, UINT32 RegionLen, UINT8 *SearchData, UINT32 SearchDataLen, UINT32 *Dst);
000330 = UINT32 TRAP_Memset(UINT8 *dst, UINT8 fillvalue, UINT32 len);
000410 = UINT32 TRAP-SlotAttach(UINT32 slot, UINT32 codeLen);
000420 = UINT32 TRAP-SlotRead(UINT8 *dst, UINT32 slot);
000430 = UINT32 TRAP_SlotWrite(UINT8 *newContents);
000510 = UINT32 TRAP_DeviceAccess(UINT32 dev, UINT32 opID, UINT8 *buf); 
000520 = UINT32 TRAP_DeviceDiscovery(UINT32 dev, UINT32 qID, UINT8 *buf, UINT32 *len);
000530 = UINT32 TRAP_DiscoveryRAM(UINT32 unknown, UINT32 dst, UINT32 len);
000540 = UINT32 TRAP_MediaReadFile(UINT8 *FileName, UINT32 SectionNumber, UINT32 Unknown, UINT32 *len, UINT8 *dst);
000550 = UINT32 TRAP_MediaSHAFileHash(UINT8 *FileName, UINT32 FileNameLen, UINT32 FileOffsetHigh, UINT32 FileOffsetLow, UINT32 *len, UINT8 *dst);
000560 = UINT32 TRAP_RunNative(UINT8 *signature, UINT32 sigLen, UINT8 *code, UINT32 codeLen);
000570 
008010 = UINT32 TRAP_DebugLog(UINT8 *txt, UINT32 len);
008020 
008030
For example "E4000230" would be the call for trap_xorblock. I don't know about the others yet. There may be more...

This is a small example of some instructions between two trap calls (trap 0x110 and trap 0x230) to show how parameters are passed:
Code:
0001:ABEC  E4000110 TRAP #110h             // call trap 0x110
0001:ABF0  581F0000 JR r31                 // return to the previous address
0001:B374  04800800 ADD r4,r0,r1
0001:B378  63BD0014 ADDIE r29,#14h         // r29 = stack pointer?
0001:B37C  BC010038 BNEZ r1,#0001B3B8
0001:B380  63BDFFF4 ADDIE r29,#FFFFFFF4h
0001:B384  E15D0000 SDW r10,r29,#0h        // first parameter: *dst
0001:B388  E17D0004 SDW r11,r29,#4h        // second parameter: *src
0001:B38C  74270002 SRLI r1,r7,#2h
0001:B390  E03D0008 SDW r1,r29,#8h         // third parameter: len
0001:B394  B7FFF8EC JAL #0001AC84        // jump into trap vector table
0001:AC84  E4000230 TRAP #230h             // execute trap 0x230 ( TRAP_XorBlock(UINT32 *dst, UINT32 *src, UINT32 len) )
I wish i already had schluppo's disassembler

Last edited by Oopho2ei; 4th October 2008 at 23:31. Reason: updated trap function name+signature
  Reply With Quote
Old 1st September 2008, 23:05   #35  |  Link
evdberg
Registered User
 
Join Date: Dec 2006
Posts: 202
Should JAL #PC+3FFF8EC not be JAL #PC-714 ? Considering the immediate value is 26 bits, and offsets are usually signed? And considering the address space is only 4MB (22 bits PC).
__________________
Developer DVD2one - http://www.dvd2one.com/
evdberg is offline   Reply With Quote
Old 1st September 2008, 23:30   #36  |  Link
Oopho2ei
Guest
 
Posts: n/a
Quote:
Originally Posted by evdberg View Post
Should JAL #PC+3FFF8EC not be JAL #PC-714 ? Considering the immediate value is 26 bits, and offsets are usually signed? And considering the address space is only 4MB (22 bits PC).
You are right (again ). I just tried to uniformly represent the immediate part as unsigned value. You would get the same result after the addition and & 0x3FFFFC. Before any memory access you need to calculate "address = address & 0x3FFFFC" anyway. (Except if you want to access 16 bit or 8 bit values then of course you wouldn't mask the least significant 2 bits). But it is much easier to read so i corrected it. I didn't record the program counter otherwise i would have just replaced #PC-714h with the result.

Last edited by Oopho2ei; 1st September 2008 at 23:56.
  Reply With Quote
Old 2nd September 2008, 17:48   #37  |  Link
Disabled
Registered User
 
Join Date: Aug 2004
Posts: 211
Very nice work, especially to Oopho2ei!
Till the 12th post in this thread I never thought this would become anything at all. (And I still don't know how much is done after the bdvm is implemented.)
First I thought about trying to continue the bdvm, but then my little C knowledge kept me from continuing. (And I never was sure if anyone else is working on it.)
So I took an easier task and tried to start with the disassembler.
I started with the source from the bdvm but now very few is left. Here it goes:
Code:
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
using namespace std;

typedef unsigned char byte; // for less typing
typedef unsigned int uint;  // dito

class BDDasm {
public:
	BDDasm();
	string dasmCmd(uint);
	void dasmFile(string, uint, uint);
	uint makeint(byte, byte, byte, byte);
private:
	uint pc;

};
// end header

int _signed(uint x)   { return (int)x; }
int _signed(int x)    { return x; }
int _unsigned(uint x) { return x; }
int _unsigned(int x)  { return (uint)x; }

BDDasm::BDDasm(){
	pc = 0;
}

uint BDDasm::makeint(byte b3, byte b2, byte b1, byte b0) {
    return ((int)b0) << 24 | ((int)b1) << 16 | ((int)b2) << 8 | ((int)b3);
}


void BDDasm::dasmFile(string fname, uint pep, uint len){
    ifstream file;
	file.open(fname.c_str());
	file.seekg(pep);
	pc = pep-6*4;
    uint I; //Instruction
	while(pc <= pep+len){
		pc +=4;
		I = makeint((byte)file.get(),(byte)file.get(),(byte)file.get(),(byte)file.get());
		cout << dasmCmd(I);
	}
		file.close();
}

string BDDasm::dasmCmd(uint I) {
	
    std::ostringstream out;
	char buffer [100];

    // instruction fetch
    uint C = I >> 0x1A; // command
    uint Rd = ((I >> 0x13) & 0x7C) >> 2; // operand X
    uint Rs1 = ((I >> 0x0E) & 0x7C) >> 2; // operand Y
    uint Rs2 = ((I >> 0x09) & 0x7C) >> 2; // operand Z
    uint C16 = I & 0xFFFF; // 16 bit constant
    int C16S = _signed(_unsigned(I) << 16) >> 16; // same signed
    uint C5 = I & 0x1F; // 5 bit constant
    uint C26 = I & 0x3FFFFFF; // 26 bit constant
    int C26S = _signed(_unsigned(I) << 6) >> 6; // same signed

	sprintf (buffer, "%6X ", pc-4);
	out << buffer;
	sprintf (buffer, "%8X ", I);
	out << buffer;
    // instructions
	string ops[0x3C] = {"NOP", "ADD", "SUB", "MUL", "IDIV", "DIVU",
            "SLL", "SRL", "SRA", "AND", "OR", "XOR", "SEQ", "SNE", "SLT",
            "SBT", "SGT", "SAT", "SLE", "SBE", "SGE", "SAE", "JR", "JALR",
            "ADDIE", "ADDI", "SUBIE", "SUBI", "SLLI", "SRLI", "SRAI",
            "ANDI", "ORI", "XORI", "SEQI", "SNEI", "SLI", "SBI", "SGI",
            "SAI", "SLEI", "SBEI", "SGEI", "SAEI", "J", "JAL", "BEQZ", 
            "BNEZ", "LHI", "LBE", "LB", "LWE", "LW", "LDW", "SB", "SW",
            "SDW", "TRAP", "SWDR", "unknown"};
			
    if(C>=0x00 && C<=0x3A){
        out << ops[C];
	}
    else {
		out << ops[0x3b];
	}
	
	if(C>=0x01 && C<=0x15){
		out << " R" << Rd << ", R" << Rs1 << ", R" << Rs2;
	}
	else if(C==0x16||C==0x17){
		out << " R" << Rs1;
	}
	else if(C>=0x18 && C<=0x3A && C!=0x2c && C!=0x2d && C!=0x39 && C!=0x2e && C!=0x2f){
		sprintf(buffer, ", #%Xh", C16);
		out << " R" << Rd << ", R" << Rs1 << buffer;
	}
	else if(C==0x2c || C==0x2d){
		sprintf(buffer, " #%Xh", (pc+(C16S& 0x3FFFFC))& 0x3FFFFC);
		out << buffer;
	}
	else if(C==0x2e || C==0x2f){
		sprintf(buffer, ", #%Xh", (pc+(C16S& 0x3FFFFC))& 0x3FFFFC);
		out << " R" << Rs1 << buffer;
	}
	else if(C==0x39){
		sprintf(buffer, " #%Xh", C26);
		out << buffer;
	}
	out << endl;
	return out.str();
}

int main() {
	BDDasm a;

    a.dasmFile("00000.svm",0x1000,0xF000);

}
Now you can laugh at my coding style. Whats left to be done is some improvements to the dasm and of course a file reader and much more.
For a start, here is the output of the hardcoded commands:
Code:
  1000 E4000110 TRAP #110h
  1004 42445356 SGT R18, R4, R10
  1008 581F0000 JR R31
  100C  4800800 ADD R4, R0, R1
  1010 63BD0014 ADDIE R29, R29, #14h
  1014 BC010038 BNEZ R0, R1, #38h
  1018 63BDFFF4 ADDIE R29, R29, #FFF4h
  101C E15D0000 SDW R10, R29, #0h
  1020 E17D0004 SDW R11, R29, #4h
  1024 74270002 SRLI R1, R7, #2h
  1028 E03D0008 SDW R1, R29, #8h
  102C B7FFF8EC JAL, #918h
  1030 E4000230 TRAP #230h
Looks quite familliar to what Oopho decoded, except for the JR and some constants.

*edit* Fixed JR handling...

*edit2* Added trivial stream handling... and watch out if someone ever tries to use the experimental BDVM, b2 is used twice in the makeint function...

*edit3* Added Offset to adress display and executed pc+r1 for jumps

*edit4* Final update with file reader and correct jumps (I hope). There is an offset for the pc of 6dwords to align with the trace in post #35, I guess thats some kind of header or something... I will post the dasm of the first 0xF000 bytes in post #40

Last edited by Disabled; 2nd September 2008 at 22:44.
Disabled is offline   Reply With Quote
Old 2nd September 2008, 18:08   #38  |  Link
chavonbravo
Registered User
 
Join Date: Aug 2007
Posts: 17
Wow, gone a few days and all this progress.
This being a source code solution for the future, it will probably turn into a cat and mouse game with future bd+ movies, where flaws will be found and attacked, and will have to be patched up.
I beleive Peter meant before about once you're past a certain point, bd+ only helps, is that at the beginning of the vm code are all the checks for a valid player, and non-modified firmware. After you successfully emulate a valid player and pass the values needed for the disc to believe it's safe, you use the bd+ code to decypher the video stream.
This is probably why anydvd comes out with updates whenever a new bd+ disc can't be played, same cat and mouse game.
chavonbravo is offline   Reply With Quote
Old 2nd September 2008, 19:35   #39  |  Link
Oopho2ei
Guest
 
Posts: n/a
Quote:
Originally Posted by Disabled View Post
40 B7FFF8EC JAL, #PC+FFFFF8ECh
I regret i didn't record the program counter (pc). Maybe i will add it later. In your case the PC = 40 so you could just evaluate the expression "PC+FFFFF8ECh"
Quote:
Originally Posted by Disabled View Post
Looks quite familliar to what Oopho decoded, except for the JR and some constants.
Please also look at some DLX disassembly examples. Like this one in section 2.2: http://heather.cs.ucdavis.edu/~matloff/DLX/Report.html and others.

Thank you for your effort. A disassembler will be helpful to study the traps.

Quote:
Originally Posted by chavonbravo
This being a source code solution for the future, it will probably turn into a cat and mouse game with future bd+ movies, where flaws will be found and attacked, and will have to be patched up.
This is likely to happen but at least people will know how BD+ works. All discs which can be decrypted up to that point can also be decrypted in the future. I can't prevent anyone from writing and selling proprietary software based on the information from this thread. But keep in mind that if the interpreter is open source everyone can make modifications maybe based on research of other licensed players. Making it more difficult to target all those different version. It gets more people involved and also improves the knowledge of the community. Finally if your open source implementation has been targeted and is not working anymore for newer discs you would "only" need to record and compare the instruction trace (and maybe register values) of both your implementation and a working licensed player to find the exploit.

Anyway:
To guide your implementation efforts i dumped the register file and the machine memory directly before the very first trap instruction (trap 110h in this case) is executed.

register file (32 register each 32 bit): http://uploaded.to/?id=8kkmfn
machine memory (4MB): http://uploaded.to/?id=nqpu3z
execute this from offset 0x1000: http://uploaded.to/?id=jqstc8

Keep in mind that the players do a lot of big endian <-> little endian conversion (improves vm speed probably) . When you look at offset 0x55D0 of the vm_mem_dump.000 you will know what i mean.

Btw i have added some more comments on the few instruction between two trap instruction calls in posting #34. Some of you have probably already guessed where the parameters are stored and in which order.
The parameters are written to the address [r29] in the order given by the function declaration. Maybe think of r29 as ESP (stack pointer) and every SDW (store double word) here would be a push. (This is not true in general of course but for calling a trap function. I haven't verified that for other traps yet.)

Last edited by Oopho2ei; 2nd September 2008 at 19:50.
  Reply With Quote
Old 2nd September 2008, 21:06   #40  |  Link
Disabled
Registered User
 
Join Date: Aug 2004
Posts: 211
Quote:
Originally Posted by Oopho2ei View Post
In your case the PC = 40 so you could just evaluate the expression "PC+FFFFF8ECh"
I added that...
Quote:
Please also look at some DLX disassembly examples...
Will do...

I tried to dissasemble your dump and something was fishy:
Code:
  1000        0 NOP
  1004        0 NOP
  1008        0 NOP
  100C        0 NOP
  1010        0 NOP
  1014        0 NOP
  1018 B0000040 J #1058h
  101C E03D0008 SDW R1, R29, #8h
  1020 E3FD0004 SDW R31, R29, #4h
  1024 D4200000 LDW R1, R0, #0h
  1028 E01D0000 SDW R0, R29, #0h
  102C D7E01154 LDW R31, R0, #1154h
  1030 E3E01150 SDW R31, R0, #1150h
  1034 B4019B0C JAL #3FAB40h
  1038 D7E01158 LDW R31, R0, #1158h
  103C E3E01150 SDW R31, R0, #1150h
  1040 E0200004 SDW R1, R0, #4h
  1044 D7FD0004 LDW R31, R29, #4h
  1048 D43D0008 LDW R1, R29, #8h
  104C 67BD001C ADDI R29, R29, #1Ch
  1050 E4000010 TRAP #10h
  1054 B3FFFFFC J #1050h
  1058        0 NOP
  105C BC00FFFC BNEZ R0, #1058h
  1060 BC01FFFC BNEZ R1, #105Ch
  1064 BC02FFFC BNEZ R2, #1060h
  1068 BC03FFFC BNEZ R3, #1064h
  106C BC04FFFC BNEZ R4, #1068h
  1070 BC05FFFC BNEZ R5, #106Ch
  1074 BC06FFFC BNEZ R6, #1070h
  1078 BC07FFFC BNEZ R7, #1074h
  107C BC08FFFC BNEZ R8, #1078h
  1080 BC09FFFC BNEZ R9, #107Ch
  1084 BC0AFFFC BNEZ R10, #1080h
  1088 BC0BFFFC BNEZ R11, #1084h
  108C BC0CFFFC BNEZ R12, #1088h
  1090 BC0DFFFC BNEZ R13, #108Ch
  1094 BC0EFFFC BNEZ R14, #1090h
  1098 BC0FFFFC BNEZ R15, #1094h
  109C BC10FFFC BNEZ R16, #1098h
  10A0 BC11FFFC BNEZ R17, #109Ch
  10A4 BC12FFFC BNEZ R18, #10A0h
  10A8 BC13FFFC BNEZ R19, #10A4h
  10AC BC14FFFC BNEZ R20, #10A8h
  10B0 BC15FFFC BNEZ R21, #10ACh
  10B4 BC16FFFC BNEZ R22, #10B0h
  10B8 BC17FFFC BNEZ R23, #10B4h
  10BC BC18FFFC BNEZ R24, #10B8h
  10C0 BC19FFFC BNEZ R25, #10BCh
  10C4 BC1AFFFC BNEZ R26, #10C0h
  10C8 BC1BFFFC BNEZ R27, #10C4h
  10CC BC1CFFFC BNEZ R28, #10C8h
  10D0 BC1DFFFC BNEZ R29, #10CCh
  10D4 BC1EFFFC BNEZ R30, #10D0h
  10D8 BC1FFFFC BNEZ R31, #10D4h
  10DC C3A00000 LHI R29, R0, #0h
  10E0 67BD115C ADDI R29, R29, #115Ch
  10E4 C3800000 LHI R28, R0, #0h
  10E8 679C1000 ADDI R28, R28, #1000h
  10EC D7BD0000 LDW R29, R29, #0h
  10F0 E3BC0000 SDW R29, R28, #0h
  10F4 D7A10000 LDW R29, R1, #0h
  10F8 C3A0001F LHI R29, R0, #1Fh
  10FC 67BDFFFC ADDI R29, R29, #FFFCh
  1100 B4000008 JAL #1108h
  1104        0 NOP
...
Are you sure its really PC += value with jumps? I don't know much asm, but I always thought they were just PC = value or something with an absolute offset like the program entry point or a memory offset... But even then that code doesn't make much sense...

Another question: In the .svm files, are there just instructions or is there data that has not length%4==0? Else I could try to implement a quick jump label generation. If there is data whose length is not dividable by 4 I'm sure I wouldn't be able to do this, as I would have to really analyze the code and decide where to start again with the dasm. Else a quick jump-search-prepass would do.

*edit* he is right and here is the (hopefully) correct dasm of the first 0xF000 bytes:
dasm.zip - 0.14MB

Last edited by Disabled; 2nd September 2008 at 22:48.
Disabled is offline   Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 15:20.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2017, vBulletin Solutions Inc.