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. |
![]() |
#21 | Link |
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.
![]() |
![]() |
![]() |
#22 | Link |
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 ... 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. |
![]() |
![]() |
#23 | Link |
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 } |
![]() |
![]() |
![]() |
#24 | Link | ||||
Guest
Posts: n/a
|
Quote:
Quote:
Quote:
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... Quote:
![]() 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 ... 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 |
||||
![]() |
![]() |
#25 | Link |
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; 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 Last edited by Oopho2ei; 6th September 2008 at 13:01. |
![]() |
![]() |
#26 | Link |
Registered User
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.
|
![]() |
![]() |
![]() |
#27 | Link | ||
Guest
Posts: n/a
|
Quote:
![]() 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 ---------------------> | |-------------------------------------------------------------------------------- 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 | | ------------------------------------------------------------------------------------------------- - 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:
Last edited by Oopho2ei; 14th September 2008 at 21:30. Reason: fixed cmd39 |
||
![]() |
![]() |
#28 | Link | |
Registered User
Join Date: Dec 2006
Posts: 202
|
Quote:
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/ |
|
![]() |
![]() |
![]() |
#29 | Link | ||
Guest
Posts: n/a
|
Quote:
![]() Quote:
![]() Last edited by Oopho2ei; 31st August 2008 at 22:09. |
||
![]() |
![]() |
#30 | Link |
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/ |
![]() |
![]() |
![]() |
#31 | Link | |
Guest
Posts: n/a
|
Quote:
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. |
|
![]() |
![]() |
#32 | Link |
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 ---- |
![]() |
![]() |
#33 | Link | |||
Guest
Posts: n/a
|
Quote:
Quote:
Quote:
Last edited by Oopho2ei; 1st September 2008 at 21:25. |
|||
![]() |
![]() |
#34 | Link | |
Guest
Posts: n/a
|
Quote:
![]() 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 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) ) ![]() Last edited by Oopho2ei; 4th October 2008 at 23:31. Reason: updated trap function name+signature |
|
![]() |
![]() |
#35 | Link |
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/ |
![]() |
![]() |
![]() |
#36 | Link | |
Guest
Posts: n/a
|
Quote:
![]() Last edited by Oopho2ei; 1st September 2008 at 23:56. |
|
![]() |
![]() |
#37 | Link |
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); } 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 *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. |
![]() |
![]() |
![]() |
#38 | Link |
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. |
![]() |
![]() |
![]() |
#39 | Link | ||
Guest
Posts: n/a
|
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:
Thank you for your effort. A disassembler will be helpful to study the traps. Quote:
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. |
||
![]() |
![]() |
#40 | Link | ||
Registered User
Join Date: Aug 2004
Posts: 211
|
Quote:
Quote:
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 ... 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. |
||
![]() |
![]() |
![]() |
Thread Tools | Search this Thread |
Display Modes | |
|
|