T1TTY
01-25-2008, 11:03 AM
Lesson 1: Assembly Basics
What is assembly language
NOTE: I will use 'ST7OP' to refer to the 'ST7 Opcodes.pdf' document
If you don't know how to read hexadecimal or the basics of computer program, you probably need to do a bit more homework before coming back to this thread. I can't go down to that level. However assembly language is a (sort of) human readable representation of the actual commands executed by the host processor.
Each processor has an instruction set. This is a list of all possible commands that the processor can execute. Each one of these is represented by one (or several) bytes, and when many of these instructions are strung together, you get a program. Each instruction can be represented as a hexadecimal number, and each of these instructions has a human-readable equivalent.
time for a simple example:
a = 10;
while(a != 0) {a--;}
this will set 'a' to 10 then continuously decrement it until it reaches 0
To implement this in assembly (we'll do it for the ST7 processor since that's what we'll be working with later) you might use:
ld a, #$0a Load 10 (0a in hex) into 'a')
loop: define an address, has no impact on the program
dec a Decrement (subtract 1 from) 'a'
jrne loop Jump to 'loop' if 'a' is not equal to 0
don't worry too much about the notation above. We'll get to that, but it is worth explaining the #$10. '#' indicates an immediate value. That means we are directly specifying the value to load. We could instead load 10 from another variable or memory address. the '$' notes that we are specifying the value as hexadecimal (as opposed to decimal)
And here is (one possible) hex representation of this command:
00:0081 a6 0a ld a, #$0a
00:0083 4a dec a
00:0084 26 fd jrne $83
The 1st column represents the address in memory where the instruction is stored, next are he hexadecimal bytes that make up the instruction, and last is the relevant assembly code. This is the format that you'll get out of most disassemblers.
Registers: (see ST7OP page 6)
Each processor has a number of registers. In general these are used to execute arithmetic functions. For the ST7 there are 6:
A, X, Y: These can be used by many operations. A is the accumulator and is the only truely general-purpose register. X and Y are index registers (used for indexing functions, see below), but can also be sued as temporary storage.
PC: The program counter. This contains the memory address of the next instruction to execute.
SP: Stack pointer. See below where I talk about the stack a bit
CC: Conditional Code Register. This contains status information about recently executed commands.
About addressing modes: (see ST7OP page 8)
There is a lot to learn about Assembly language, and I don't have time to teach it all. You will need to know some common addressing modes though.
Immediate: A value is specified directly (like #$0a above)
Direct: A memory address is specified. Load the value from there
Indexed: add an immediate value to the contents of another register, and load the value from the resulting memory address
Indirect: add the value from the memory address given to the contents of another register, and load the value from the resulting memory address
Relative: Only used for jumps. This specifies that we add the value to the PC and use that as the next address to execute.
Now let's look at the assembly code above again:
ld a, $#0a <- uses immediate addressing to load the value $0a into the accumulator. PC is now $83
dec a <- subtracts one from the accumulator. When the value reaches zero, the 'Z' bit in the CCR is set. PC is now $84
jrne $83 <- check the Z bit of the CCR. If it is NOT set, then jump relative to $83. PC is now $86. Note that the opcode is '26fd'. '26' is the command for jrne'. 'fd' is the relative address to jump to. fd in hex is -3 (8bit signed) so we add $86 + (-3) and end up with $83 as the address to jump to.
Stack Pointer:
With only 3 registers available for arithmetic functions, we need more room to store variables. We can directly store them in RAM, but that only works for simple cases, since we need to know ahead of time where in RAM to put them. The stack-pointer contains a RAM address which can be used to store a variable. It must be initialized on startup (in the case of the ST7 family, it is initialized to the last byte of available RAM). a 'push' instruction pushes the value of a register into the address specified by the SP, then decrements the SP (so it points at a new available memory location). A 'pop' instruction increments the SP and retrieves the value specified by the SP into the specified register.
Homework:
Since we haven't talked much about the ST7/ST19, these are very simple examples. We'll see more as things progress.
1) Run this code through the disassembler, and try to tell me what it does:
NOTE: There was a bug in the original code. Make sure you are using the following code:
A60588855A894285A30126F8
2) write assembly code using ST7OP (give both the instruction and hexadecimal opcode) for the following:
a=0
x = 1;
while(a<512) {x = (x * 2) % 256;a = a + 1;}
Hint: you can ignore the '%256' It only means that we only care about the lowest 8 bits of X .
If you are familiar with Assembly code, please don't post the answers right away (or just PM them to me if you want me to check). I would rather those who aren't as familiar with assembly try to work this out. If you'd like to post answers, please post them in the 'homework assignment' thread so others can work them out on their own (please add the assignment number in your post to make it obvious which one you are posting about since there will be more to come). Feel free to ask questions here.
Update:
I had a question on using pdisassembly and why the code doesn't match what is in the ST7 manual.
There are several notations which are used interchangeably when looking at ST7 code, and you'll need to be able to recognize all of them.
The common ones are as follows:
bne == jrne
bz == beq == jrz == jreq
(there are several in the same category in the conditional branch case)
ld a, x == txa
ld x, a == tax
(NOTE: the 'ld' is actually poor terminology for this set of commands, but we'll get to that later)
call == callr == jsr (sort of I'm merging relative and absolute adressing here)
j == jp == jra == bra (again I'm merging addressing modes)
cp == cmp
Lesson 2: ROM 102
About the ROM102
The ROM102 uses an ST19XL processor (which is an enhanced version of the ST7 you have documentation for). The processor is based on a Motorola 6805 instruction set.
Specs:
4KB or RAM
18kB of EEPROM (electronically erasable/programmable ROM)
96kB of ROM
Because the address space is only 16bit (65kB), the ROM102 introduces a segmented (often called paged) memory layout. You'll see memory addresses like '00:8397' Here '00' is the page and '8397' is the address on that page.
The memory layout is as follows:
0000-0FFF : RAM
3000-307F : OTPROM
3080-37FF : EEPROM
4000-7FFF : UROM
00:8000-FFFF : ROM
01:8000-FFFF : ROM
02:8000-BFFF : ROM
80:8000-BFFF : EEPROM
Note that segments only apply to memory > 8000. You can access memory in the 0000-7FFF range from within any segment. More on segments below.
Also, there are some holes in there. specifically between 1000-2FFF and 3800-3FFF. I have heard that the MAPROM lives in some of that area, but regardless, it is protected and there is no easy way to see what is in there. For our uses, we'll just assume it doesn't exist.
RAM:
Different parts of RAM have different uses, but the important things to know for now is that 00-1f are unaccessible to us, and 0EF8-0FFF are normally reserved for the stack. The processor will also use 0DF8-0EF7 for storing I/O so you might be interested in snooping this at some point, but we can talk about that when playing with the simulator if needed.
OTPROM:
The OTPROM is one-time-programmable. That means that the card was initialized at the factory with all 0's in this area, and during programing some bits have been set to 1. Once set to '1' the bit can never be flipped back to '0'. This area is not very relevant to our endeavors, but if you ever hear about a 'marked' card, it means that one of these OTP bits was set, indicating that the card was detected running something it shouldn't. A marked card is a candidate for a loop ECM, but again, that doesn't really apply to us.
EEPROM:
The EEPROM can be programmed (and erased) on the fly, and there are specific commands which write to it. This is how the provider can change the programming and functionality of the card (and how they cause us all of our issues)
UROM:
The UROM is where the core code for the card is stored. All commonly used routines can be found here. Note that the ROM can never be changed once the card leaves the factory. The providers have foreseen the need to patch the ROM functions, and so some of the routines in the ROM can be superseded by code within the EEPROM (more on that later). The UROM is accessible from all memory segments.
Paged (segmented) memory:
There are 3 pages of ROM and 1 page of EEPROM. The 00 page contains more (slightly less used) functions needed to run the card. the 01 and 02 pages seem to contain random data and to be unused. The 80 page is where EEPROM lives, and this gets updated whenever the provider decides to do so. To access segmented memory, 2 new registers were introduced: The CSR and DSR. the CSR is the Code Segment Register, and the DSR is the Data Segment Resgister. You can expect that these registers will hold one of the following values:00, 01, 02, or 80. The DSR is set and read using load commands. When using a 'load' to retrieve a value from memory, the DSR is used to determine which page to load the data from. The CSR is used to determine where the next instruction will be read from (it is like an extension to the Program Counter). It can be read, but not written directly. Instead, it is set when a 'call' (often known as jsr or jump to subroutine) is executed. I don't believe there is any other way to change the CSR.
So to make more progress, we need to know a little more about assembly language.
We've talked about Load operations, but there is also an operation known as 'Transfer' The Transfer operation copies data from one register to another (i.e. directly from a to x). These are (confusingly) listed in ST7OP page 56 as 'Load' instructions, and it is ok to think of them like that, but there is a caveat to the 'Transfer' instructions: Unlike loads, they do not affect the CCR. You will often see transfer instructions listed as either 'TAX' (transfer A to X) or 'ld x, a' (load x with the value of a). These are equivalent. I prefer the 'tax' notation since it makes clear that the operation works differently (if only slightly) from a load.
Above I also mention CALL/JSR routines. There are 2 main ways to change where the program is executing:
Jump: This just changes the PC and the code proceeds on its way (at the new location)
JSR/RET: This pushes the current value of the PC onto the stack before jumping to the specified address (actually it pushes the address of the following instruction onto the stack). Then when a RET (return) is called, the original PC is popped off the stack and reloaded into the PC and the processor continues where it left off (just after the JSR routine)
As mentioned previously, there are different addressing modes. Both JSR and JMP can use either relative or absolute addressing. Relative addressing is useful for jumping within +/- 127 bytes of the current PC. Absolute addressing allows for jumping anywhere in memory.
There is also a special 'PJSR/PRET' set of commands. These are 'Paged Jump to Subroutine' and 'Paged Return' They allow specifying the page (segment) number along with the address thus allowing to jump from one page to another. To my knowledge there is no 'Paged Jump' instruction, so when jumping between pages, a PJSR is the only way to do it.
A quick note here:
JSR and CALL are the same. I will often use JSR though the ST7 manual uses 'CALL'
ST7OP distinguishes between CALL and CALLR. I will use JSR for either one. The only difference is the addressing mode
ST7OP uses JP and JRA. Again, these are identical except for the addressing mode, and I'll usually use 'JP'. You may see 'BRA' (branch) or 'J' (Jump) which is the same thing.
You may see CALLP or ECALL somewhere (it's an instruction not in ST7OP). This is equivalent to what I call JSRP or PJSR. They're all the same thing.
MAP functions
The MAP functions are mostly cryptographic functions. They are in a part of the ROM which is very difficult to get at, so it isn't really possible to read them from the ROM to disassemble and understand them (though there are claims this has actually been done). MAP functions are called with a specific set of instructions:
A6 44 ld a, #$44
8D 00 A8 22 jsrp #$00, $A822
ld a,#$44 loads 0x44 into 'A'. this is the map function to execute (MAP44 in this case). JSRP #$00, $A822 jumps to the MAP handler. This lives in rom at 00:A822. Each MAP function expects input data to be placed at a specific point in RAM and the output will be placed at a specific point in RAM (sometime overwriting the input)
We know what a lot of these MAP functions do already. You can find a list of known MAP functions in 'The Nagravision2 FAQ v1.02' on Page 2. Note that we've recovered the Map3B and Map57 although it isn't straight-forward to define their purpose.
Bug Tables
It is important before digging into more examples to understand bug tables. The providers realized that they might have bugs in their code (or the need to modify how code works) and so they implemented what is known as a bug-table. Basically the Bug-Table is a lookup table that allows certain functions that are implemented in ROM to be overridden by code in EEPROM (effectively replacing the ROM code with some EEPROM code). We'll need to get into how this works later, but for now you should be able to recognize code that has a hook.
Here is an instruction from the UROM:
8D 00 5F 92 ecall #0, BUG_CATCHER ; See if there is a PATCH for this routine
the code at 5F92 (again, note that ecall is the same a pjsr, you'll need to get used to having multiple names for the same instruction) does some trickery to compare the PC which was pushed on the stack with a list of values in EEPROM. If a match is found, the call will rediretc to EEPROM and the original routine will never be executed. Not every ROM function has a hook for a bug-table though. Since we're not going to be writing a lot of code for the ST19 we don't really care how to implement a Bug or which functions can support them. We just need to know how to check whether there is an EEPROM replacement function or not so we disassemble the right thing. But we'll get to that.
Homework:
3) What is the value of 'a' when the program counter reaches 008a:
$0080=A61097AD075A26FB9D9D9D9DCD00924C
$0090=819D4C90859085810000000000000000
0080: A6 10 ld a, $#10
0082: 97 tax
0083: AD 07 jsr $8c
0085: 5A dec x
0086: 26 FB jrne $83
0088: 9D nop
0089: 9D nop
008a: 9D nop
008b: 9D nop
008c: CD 00 92 jsr $92
008f: 4C inc a
0090: 81 ret
0091: 9D nop
0092: 4C inc a
0093: 90 85 pop y
0095: 90 85 pop y
0097: 81 ret
Some of you may want to use a simulator for this. That's ok (though It'd probably be better practice to work it out by hand), but make sure you understand WHY you get the answer you do.
Lesson 3: Using the simulator, and tracing some code
Using the simulator and analyzing some code.
Unfortunately I've been unable to get the simulator to run sufficiently on Wine, so you'll need a Windows system for this lesson.
To look at more complicated examples, we will learn to use the simulator now. The simulator allows us to load a ROM image, apply patches, and execute code. Since we haven't looked at EMMs yet, we're just going to learn to use the simulator for simple examples, and won't be running real commands through it yet.
Start up the Simulator. select 'Rom102.bin' as the rom and 'R102Rev10B.bn102' as the EEPROM
The main window can be used to send commands to the simulator, but they must be in a specific format, and we're not quite ready for that yet.
Instead, select Debug->Show Debug Window
You are now looking at the contents of RAM. You can see the values of various registers on the left.
We'll now load homework assignment (1) into RAM:
Select Memory->Patch Memory
In the new window enter:
$0080=A60588855A894285A30126F800000000
and press 'Apply patch'
note that I added a bunch of 0's at the end. The patch format which is used is:
$<addr>=<16bytes of data>
OR
$<addr><n (in hex)><n bytes of data>
So we could also have specified:
$0080CA60588855A894285A30126F8
You can see the changed bits in the RAM page now.
Next, click on the 'PC' (on the left) and type in '0080'
press 'F5', or the 'Step cycle by Cycle' button once
you'll see that the disassembled code appears in the bottom pane, that the accumulator (A register) has changed to 5, and the the PC has changed to 0082
This behavior is different than what you'll see if you ever use gdb in that gdb tells you the upcoming instruction when it stops, not the one it just executed. That is usually more useful, and my preference, but we use what we've got.
You can continue to pree F5 and step through the results until the program counter reaches '8c' and the program is done.
Now, let's look at something more interesting. We're going to use the simulator to watch the bug-table work.
Rather than executing a command, and entering the bug-table the normal way, we're going to jump right to the entry point. Basically, we'll load the EMM command into A, and starting code execution at the right point such that we execute the EMM processing bug.
before starting, make sure you have the ROM102 image loaded, and are using to 10B EEPROM image.
press 'reset' (F2) to clear out any current state.
click on 'A' and enter E3 (this is an E3 EMM which is implemented via an EEPROM override. We won't get far enough to care about the value of E3, but if you want to continue tracing after the tutorial is done, it is a good value to use)
click on 'PC' and enter 9595 (this happens to be the instruction before a call to the bug-handler in the EMM processing code)
scroll down in the RAM window to the end (so you can see 0FFF). We want to watch the stack in this example
start stepping through the code.
Also, you might want to look at the 102 disassembly as well, as it is quite well documented for this function.
here's what you should see:
00:9595 8D 00 5F 92 XJSR #$00,$5F92 DSR=00 A=E3 X=00 Y=00 SP=0FFC CCR=??------
-> 5f92 is the address of the bug handler. Note that $0FFE:$0FFF contain 0x9599. This is the address we would return to if we called a 'ret' right now, and it is the address we're going to try to match in the bug-table.
<<TRAP>>
00:5F92 89 PUSH X DSR=00 A=E3 X=00 Y=00 SP=0FFB CCR=??------
-> save off x, a, cc. this is very common in subroutines
00:5F93 88 PUSH A DSR=00 A=E3 X=00 Y=00 SP=0FFA CCR=??------
00:5F94 8A PUSH CC DSR=00 A=E3 X=00 Y=00 SP=0FF9 CCR=??------
00:5F95 B6 05 LDA $05 DSR=00 A=00 X=00 Y=00 SP=0FF9 CCR=??----Z-
00:5F97 A4 9E AND #$9E DSR=00 A=00 X=00 Y=00 SP=0FF9 CCR=??----Z-
-> don't worry about this. Just checking some bits to see if the bug-handler is enabled.
00:5F99 27 04 BEQ $5F9F DSR=00 A=00 X=00 Y=00 SP=0FF9 CCR=??----Z-
00:5F9F C6 31 77 LDA $3177 DSR=00 A=AF X=00 Y=00 SP=0FF9 CCR=??---N--
00:5FA2 CE 31 76 LDX $3176 DSR=00 A=AF X=01 Y=00 SP=0FF9 CCR=??------
00:5FA5 27 03 BEQ $5FAA DSR=00 A=AF X=01 Y=00 SP=0FF9 CCR=??------
00:5FA7 C6 31 78 LDA $3178 DSR=00 A=B9 X=01 Y=00 SP=0FF9 CCR=??---N--
->$3176 is an EEPROM address. It controls whether we use the standard or alternate bug-table. for a given revision, this will be fixed, and in 10B 3176 contains '1'. '0' means we use the 'alt' table, anything else means we use the normal table.
$3178 contains the length of the standard bug-table (and $3177 contains the length of the alt bug-table). the table-length is 0xB9
00:5FAA 88 PUSH A DSR=00 A=B9 X=01 Y=00 SP=0FF8 CCR=??---N--
00:5FAB C6 30 58 LDA $3058 DSR=00 A=FF X=01 Y=00 SP=0FF8 CCR=??---N--
00:5FAE C4 30 5E AND $305E DSR=00 A=FF X=01 Y=00 SP=0FF8 CCR=??---N--
00:5FB1 4C INC A DSR=00 A=00 X=01 Y=00 SP=0FF8 CCR=??----Z-
00:5FB2 26 14 BNE $5FC8 DSR=00 A=00 X=01 Y=00 SP=0FF8 CCR=??----Z-
00:5FB4 27 6B BEQ $6021 DSR=00 A=00 X=01 Y=00 SP=0FF8 CCR=??----Z-
->There are 2 bug-catcher routines. They work basically the same way, but one computes checksums and the other doesn't. Unfortunately, we're using the checksum version which is a bit more complicated. The checksums are there (I think) to make it harder to hack the bug-table, so I'm going to ignore the checksum stuff mostly...
00:6021 AE FB LDX #$FB DSR=00 A=00 X=FB Y=00 SP=0FF8 CCR=??---N--
-> $FB is equal to -5. Each entry in the table is 5 bytes long.
00:6023 A6 00 LDA #$00 DSR=00 A=00 X=FB Y=00 SP=0FF8 CCR=??----Z-
00:6025 88 PUSH A DSR=00 A=00 X=FB Y=00 SP=0FF7 CCR=??----Z-
-> the value in 'a' is checksum related, we can ignore it
00:6026 20 16 BRA $603E DSR=00 A=00 X=FB Y=00 SP=0FF7 CCR=??----Z-
00:603E 31 E7 01 STA $01,SP DSR=00 A=00 X=FB Y=00 SP=0FF7 CCR=??----Z-
00:6041 9F TXA DSR=00 A=FB X=FB Y=00 SP=0FF7 CCR=??----Z-
00:6042 AB 05 ADD #$05 DSR=00 A=00 X=FB Y=00 SP=0FF7 CCR=??-H--ZC
-> A now contains the index of the next bug (note that 'a' is 0, so we're at the beginning now)
00:6044 97 TAX DSR=00 A=00 X=00 Y=00 SP=0FF7 CCR=??-H--ZC
00:6045 31 E3 02 CPX $02,SP DSR=00 A=00 X=00 Y=00 SP=0FF7 CCR=??-H---C
-> we're comparing 'x' to the value stored in $SP+2 ($0FF9) which is the length of the bug table that we stored earlier (0xB9)
00:6048 25 2E BCS $6078 DSR=00 A=00 X=00 Y=00 SP=0FF7 CCR=??-H---C
-> x < 0xb9, so we continue processing
00:6078 D6 31 7B LDA $317B,X DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??-H---C
00:607B 31 E1 08 CMP $08,SP DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??VH-N-C
00:607E 26 A8 BNE $6028 DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??VH-N-C
-> The bug table starts at $3179. it is composed of pairs of addresses as follows:
<address to override> <where to jump>
<address to override> is 3 bytes (CSR + 2 bytes for the address)
<where to jump> is 2 bytes (CSR must be 0x80 since we are patching into EEPROM, so we don't include it, just the actual address)
so we load the LSB (3179 + X (which is 0 here) + 2 (0 is CSR, 1 is most-significant-byte, 2 is least-significant byte), and compare it against the value in $SP+8 ($SP is 0xFF7 + 8 = $0FFF, and the value in memory there is 99..which is the LSB of the address we're trying to map). Here A contains 1A != 95 so we didn't match and we will try again.
00:607E 26 A8 BNE $6028 DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??VH-N-C
00:6028 31 E8 01 EOR $01,SP DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??VH---C
00:602B D8 31 79 EOR $3179,X DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??VH---C
00:602E D8 31 7A EOR $317A,X DSR=00 A=9A X=00 Y=00 SP=0FF7 CCR=??VH-N-C
00:6031 20 0B BRA $603E DSR=00 A=9A X=00 Y=00 SP=0FF7 CCR=??VH-N-C
00:603E 31 E7 01 STA $01,SP DSR=00 A=9A X=00 Y=00 SP=0FF7 CCR=??-H-N-C
->This is all checksum related stuff, but we are now back at $603E and ready for another round. At this point it would be boring to press the stepping button until we found match, so let's set a breakpoint (or 2). Right click on the Breakpoint window, select 'Add Breakpoint, and enter '604A'. Repeat and enter 6080. The simulator will stop whenever the PC reaches a breakpoint.
If you look carefully at the trace so far, $604A is the address right after the compare between 'x' and 0xB9. If we reach this we have traversed the entire table and haven't found a match (there is no bug for this address).
$6080 is the address right after the compare between 'a' and 0x99 (the LSB of our address). If we get here, we have at least a partial match of the address.
Now press the 'start' button
the screen will scroll for a few seconds and end with:
00:607E 26 A8 BNE $6028 DSR=00 A=99 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
->the PC is at 6080 so we found a match for the LSB of the address. 'X' is at 0x32 (this is the current index) so the address we loaded was
$3179+2+$32 = $31AD Select View->EEPROM and scroll down to $31A0. We can see the $31AB:$31AD contains 00:9599 which is the address we're comparing against, so we found a match. The next 2 bytes are '81 E2' so we should expect that we'll jump to 80:81E2. We'll now follow the code and see if we're right. Use the single-step mode again.
00:6080 31 E8 01 EOR $01,SP DSR=00 A=50 X=32 Y=00 SP=0FF7 CCR=??-H----
00:6083 31 E7 01 STA $01,SP DSR=00 A=50 X=32 Y=00 SP=0FF7 CCR=??-H----
->This is more checksum stuff. just ignore it.
00:6086 D6 31 7A LDA $317A,X DSR=00 A=95 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:6089 2A E6 BPL $6071 DSR=00 A=95 X=32 Y=00 SP=0FF7 CCR=??-H-N--
->This is checking whether the MSB of the address is less than 0x80. If it is, we can ignore the CSR byte (remember that all addresses from $0000-$7FFF are accessible from any segment). In this case the MSB is 0x95, so we can't ignore the CSR byte.
00:608B 31 E1 07 CMP $07,SP DSR=00 A=95 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
00:608E 26 A3 BNE $6033 DSR=00 A=95 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
->check whether the MSB matches our address (it does)
00:6090 31 E8 01 EOR $01,SP DSR=00 A=C5 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:6093 31 E7 01 STA $01,SP DSR=00 A=C5 X=32 Y=00 SP=0FF7 CCR=??-H-N--
->more checksum stuff
00:6096 D6 31 79 LDA $3179,X DSR=00 A=00 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
00:6099 31 E1 06 CMP $06,SP DSR=00 A=00 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
00:609C 26 9D BNE $603B DSR=00 A=00 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
->check whether the CSR byte matches our address (it does)
00:609E 31 E6 04 LDA $04,SP DSR=00 A=E3 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60A1 31 E7 02 STA $02,SP DSR=00 A=E3 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60A4 31 E6 03 LDA $03,SP DSR=00 A=C0 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60A7 31 E7 01 STA $01,SP DSR=00 A=C0 X=32 Y=00 SP=0FF7 CCR=??-H-N--
->remember how we pushed x, a, cc when we entered the bug handler? We need to get them back so they're available to the bug routine. This has just loaded those values from deep in the stack and pushed duplicates on.
00:60AA D6 31 7C LDA $317C,X DSR=00 A=81 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60AD 31 E7 04 STA $04,SP DSR=00 A=81 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60B0 2B 11 BMI $60C3 DSR=00 A=81 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60C3 A6 80 LDA #$80 DSR=00 A=80 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60C5 31 E7 03 STA $03,SP DSR=00 A=80 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60C8 D6 31 7D LDA $317D,X DSR=00 A=E2 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60CB 31 EE 05 LDX $05,SP DSR=00 A=E2 X=00 Y=00 SP=0FF7 CCR=??-H--Z-
00:60CE 31 E7 05 STA $05,SP DSR=00 A=E2 X=00 Y=00 SP=0FF7 CCR=??-H-N--
->And now we've placed 80:81E2 onto the stack, and loaded the orginal 'x' into itself.
00:60D1 86 POP CC DSR=00 A=E2 X=00 Y=00 SP=0FF8 CCR=??------
00:60D2 84 POP A DSR=00 A=E3 X=00 Y=00 SP=0FF9 CCR=??------
->Pop A and CC, and if you look at the SP you'll see that 0xFFC contains 80:81E2
00:60D3 87 XRTS DSR=00 A=E3 X=00 Y=00 SP=0FFC CCR=??------
->we execute a 'ret' and the PC is now at 80:81E2 and we're running the override function. Note that the original address 00:9599 is still on the stack. The bug routine could play a trick like I used in Lesson 2 to not return to 00:9599, or it could terminate normally and allow the code at 00:9599 to continue (i.e. we can either replace the function with the bug code, or we can pre-process data in the bug-code and then execute the original function when we're done
The only homework for this assignment is to try to understand what we went through here. Remember to use the Annotated ROM102 disassembly if you are confused. This is quite a bit more complicated than what we've done so far, but it is important that you understand what we did so you'll have a chance to understand what code is doing when you don't have any help (for instance the code at 80:81E2 is undocumented, and you'd need to figure out how it works without the help of the annotated Rom102 Disassembly).
Lesson 4
----------
EMMs and ECMs:
Note: I'll refer to the Nagra2 Echostar FAQ v0.3.txt in this lesson, which I will abbreviate FAQ from now on.
A few definitions:
Card Revision: This is the version of the EEPROM code currently loaded on the card. In the past (aside from the keys) EEPROM was usually only changed by a Revision update. Currently DN is using Rev10C and BEV is using Rev248. These are virtually equivalent.
EMM is an 'Entitlement Management Message' These can be used to determine whether a card is allowed to decrypt a given stream. For our purposes, the EMM is used to either update EEPROM or to change the decryption keys.
ECM is an 'Entitlement Control Message' This is used to generate valid CW keys (see below)
You'll also hear about ECM as 'Electronic Counter Measure'. This is when the provider sends a set of commands to try to kill hacked cards. They occasionally find a way to execute code that executes differently on a hacked card than a legally subscribed card, and they'll use this discrepancy to set the card to run in an infinite loop such that it (usually) can't be recovered. We're not going to talk about these.
There is a large list of EMMs (which can be found in FAQ), but we're really only interested in a few:
B1: the payload will be stored in RAM (at 0093) and will be executed as code. In the past a B1 was used to update EEPROM for new revisions. Recently the B1 has been used to update the 00/01 decryption keys
E3: this is a newish EMM which is processed in the bug-handler. It is an efficient way to update EEPROM. The payload contains memory-addresses and data to be loaded at that address (much like applying a patch as described in lesson 3).
E0: the EMM E0 is used in some cases to update the 00 or 01 decryption keys.
42: Also known as DT06, this EMM updates the 00 or 01 decryption keys
9F: I haven't looked at it yet. This is something new in Rev10C/Rev249. It is part of what caused all of the breakage.
High-level analysis of decrypting a channel:
The raw stream data (the video and sometimes the audio) are encrypted using an algorithm called CSA. It is a standard algorithm for all DVB providers (it has nothing to do with Nagra2), and some DVB cards (like the Nexus) have hardware to decrypt the stream (for budget cards we use FFdeCSA for decrypting this). To run the CSA algorithm, a key (called the codeword or CW) is needed, and the reason you are reading all this is to figure out how to get the correct CW. Currently, CWs are valid for 30 seconds each. There is a pair of them (noted as odd and even) and they change alternately every 15 seconds. The odd key is changed while the even one is in use, and vice-versa so a valid key is always available (so the stream switches back and forth using the odd and even keys such that a valid key is always in use.
The CWs themselves are also encrypted (and this is where Nagra2 comes into play).
The CW update is sent in an ECM. The key needs to be decrypted using either the 00 or 01 key and (usually) the M1 key. In recent revisions, the result is then morphed using some MAP functions to generate a correct CW, and in the very last revision (Rev148/Rev10C) the raw data is morphed prior to running the decrypt as well.
The 00 and 01 keys change less frequently, and recently this has been done using B1 EMMs. The B1 is normally a 'morph' which manipulates the current EMM into a different one (via code and/or calls to ROM/EEPROM) becoming an E0 or 42 EMM. This morphed EMM is executed as soon as the B1 processing is complete, and actually updates the 00 or 01 keys
Sending a command to the simulator
The smartcard has an interface by which it sends and receives commands. We can't just take the raw stream from the satellite and pass it to the card, but instead need to format it 1st.
The format of the data is as follows:
<header><length><data><end><lrc>
I'm not sure all the possible values of the <header>, but we can use '21 40 06 DA CA 00 00' for our purposes
<length> is 1 byte and is the length of the data
<end> is 02
<lrc> is a checksum. There is no reason to know how to calculate it here. The simulator can calculate it for us.
the data is further divided as:
<cmd><len1><id><packet>
<cmd> is the packet type. There is a list of them in the Nagra2 Echostar FAQ 0.3 (from now on abbreviated as FAQ) starting on line 312. Since we'll be focusing on EMM commands, we'll expect this to be 04
<len1> is the length of the current packet. it is possible to store multiple packets inside a single data, but I don't believe any providers do so. In this case len1 is always length-2
<id> is 2 bytes and is the provider id. This defines which keys to use to decrypt the packet.
<packet> is an encrypted packet to process. It must be decrypted (by the smartcard) before it can be used.
Here is a raw EMM I captured using vdr-sc:
[hexdata.emm] EMM pid 0x0120
[hexdata.emm] 0000: 82 70 6c 00 00 00 00 00 04 65 00 01 82 00 10 92
[hexdata.emm] 0010: 02 b6 f1 8f cb 93 0a 47 76 77 36 07 99 eb 74 8c
[hexdata.emm] 0020: 8f 30 77 02 f9 c3 a0 3f c0 66 3e f9 90 27 e1 8d
[hexdata.emm] 0030: 04 14 cf 22 de 31 2a e6 f7 fe ba 35 1a c8 5f 7f
[hexdata.emm] 0040: 0c 2e b5 eb ff 64 bc b0 07 55 80 3b f4 72 c9 09
[hexdata.emm] 0050: 88 0d df 3e 5b 8f 48 5d 41 18 ea 84 4b 3a c6 a0
[hexdata.emm] 0060: 7f 2f f0 eb df 0f 4b 91 e9 a9 c1 11 c0 22 52
the <header>, <length>,<end>, and <lrc> are not present in the packet.
the <data> starts at 0008 for an EMM packet
so putting it all together:
<header>21 40 06 DA CA 00 00
<length>67 (remember this is len1+2 and len1 is 65 (see below)
<data>04 65 00 01 82 00 10 92
02 b6 f1 8f cb 93 0a 47 76 77 36 07 99 eb 74 8c
8f 30 77 02 f9 c3 a0 3f c0 66 3e f9 90 27 e1 8d
04 14 cf 22 de 31 2a e6 f7 fe ba 35 1a c8 5f 7f
0c 2e b5 eb ff 64 bc b0 07 55 80 3b f4 72 c9 09
88 0d df 3e 5b 8f 48 5d 41 18 ea 84 4b 3a c6 a0
7f 2f f0 eb df 0f 4b 91 e9 a9 c1 11 c0 22 52
(note that <cmd> is 04, <len1> is 65, <id> is 00 01)
<end>02
<lrc>we don't care, let the simulator compute it
To test it, do the following:
Close the Debug window if you have it open
Make sure you have the Rom102.bin and 10B EEPRM loaded.
Press 'Reset'
Press 'Start'
Select 'Add LRC'
Paste this into the command window:
21406Da0ca000067
046500018200109202b6f18fcb930a477677360799eb748c8f 307702f9c3a03fc0663ef99027e18d0414cf22de312ae6f7fe ba351ac85f7f0c2eb5ebff64bcb00755803bf472c909880ddf 3e5b8f485d4118ea844b3ac6a07f2ff0ebdf0f4b91e9a9c111 c02252
02
Press 'Send'
You should see:
> 21 40 6D A0 CA 00 00 67 04 65 00 01 82 00 10 92 02 B6 F1 8F CB 93 0A 47 76 77 36 07 99 EB 74 8C 8F 30 77 02 F9 C3 A0 3F C0 66 3E F9 90 27 E1 8D 04 14 CF 22 DE 31 2A E6 F7 FE BA 35 1A C8 5F 7F 0C 2E B5 EB FF 64 BC B0 07 55 80 3B F4 72 C9 09 88 0D DF 3E 5B 8F 48 5D 41 18 EA 84 4B 3A C6 A0 7F 2F F0 EB DF 0F 4B 91 E9 A9 C1 11 C0 22 52 02 D9
Performing EEPROM Clean-up...
EEPROM Clean-up Done.
MapFunc $02
MapFunc $07
MapFunc $04
MapFunc $46
MapFunc $0B
Export = 27F9071D67A6FD9CB02B4DC4449467D794E5FA9B0F0D19DE76 75AE09035AEA8287047975C54380E0F73CFC399E64513D0313 FF9CFC46B494E9EEBEAC18A144BAA5736867D8B48A1409AC04 F34DE640127CD32C43B3C64EC97E149F0DC02944A5
MapFunc $02
MapFunc $49
MapFunc $48
MapFunc $0D
MapFunc $0C
MapFunc $02
MapFunc $07
MapFunc $04
MapFunc $3C
MapFunc $0B
Export = 2F84E5DEB8696876000113DB0001171A7789B1A8009D71804E 4E010142BCBCE2DE963F6891178251194A62D404904B8A8E2D CB35A072D7AAB98AD983AE1C90934E4ED68127A8008D008172 4E4AE89FE79F5A2AEA4E4E4E4E7100A60DCC956981
< 12 40 04 84 00 90 00 42
Now, press 'Stop'
This means the emm was processed (whether it passed or failed is specified in the result value, and I'm not going to worry about that right now. The value under the 2nd 'Export' is the decrypted EMM. You can see that it matches the one I retrieved from vdr-sc:
[nagra.rawemm] Nagra2 RAWEMM
[nagra.rawemm] 0000: 2f 84 e5 de b8 69 68 76 00 01 13 db 00 01 17 1a
[nagra.rawemm] 0010: 77 89 b1 a8 00 9d 71 80 4e 4e 01 01 42 bc bc e2
[nagra.rawemm] 0020: de 96 3f 68 91 17 82 51 19 4a 62 d4 04 90 4b 8a
[nagra.rawemm] 0030: 8e 2d cb 35 a0 72 d7 aa b9 8a d9 83 ae 1c 90 93
[nagra.rawemm] 0040: 4e 4e d6 81 27 a8 00 8d 00 81 72 4e 4a e8 9f e7
[nagra.rawemm] 0050: 9f 5a 2a ea 4e 4e 4e 4e 71 00 a6 0d cc 95 69 81
So, let's rerun the same EMM, but add a breakpoint in the EMM processing code.
do another 'Reset'
Select 'Add Breakpoint' and add 00:9593 as well as 00:959B (9593 is right at the beginning of the EMM processor (just before the bug-handler call), and 00:959B is right after the bug-handler returns (we can't use 00:9599 because the simulator won't honor a breakpoint at a RET location)
Start the simulator, and paste in the same command we pasted before (make sure the 'add lrc' is selected
It should pop open the debug window after a few seconds.
The PC should be 9593, and 'A' should contain B1. we know from lesson 3 that 'A' will contain the EMM type at this point, so this must be a B1 EMM. Take a look at the RAM window between 0080 and 00E0. Notice that it looks very similar to the decrypted EMM above (in fact it is identical except for the first 5 bytes. If we wanted to, we could install a decrypted EMM here and start processing from 00:958F, and skipp all the header and decryption stuff. But let's continue. I happen to know that a B1 isn't handled by the bug-handler, so close the debug window. The debug window should pop back open shortly with the PC set at 959B. We have now passed through the bug-handler, and now we'll see how the B1 gets handled.
You should see on your screen:
80:80E1 87 XRTS DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H-N-C
<<EMMCMD01>>
00:9599 A1 01 CMP #$01 DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H-N--
-> Now that is helpful. The simulator identified the EMM-01 processing code. Well, we're looking for the B1 handler, so just step through until you see:
<<EMMCMDB1>>
00:A1B8 A1 B1 CMP #$B1 DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H--Z-
-> We've found the B1 handler. So let's see what it does.
00:A1BA 26 12 BNE $A1CE DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H--Z-
00:A1BC 0B 62 0C BRCLR5 $62,$A1CB DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H--ZC
00:A1BF 1C 61 BSET6 $61 DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H--ZC
00:A1C1 18 64 BSET4 $64 DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H--ZC
00:A1C3 BD 93 JSR $93 DSR=00 A=B1 X=00 Y=97 SP=0FF7 CCR=??-H--ZC
00:0093 A8 00 EOR #$00 DSR=00 A=B1 X=00 Y=97 SP=0FF7 CCR=??-H-N-C
-> So basically it does a check on memory in $62 (we haven't seen the BRCLR command before, but this checks whether bit 5 of the value in $62 is 0 and jumps to A1CB if so (it wasn't so we didn't), then sets a few status bits (BSET just sets a specific bit of a specific memory location) and then it jumps into RAM at $93 (which is the byte immediately following the 'B1' in the EMM.
There isn't too much point in following the B1 through, since it just does a lot of random stuff to morph itself. The key is that it will eventually jump back to $9569 and reprocess itself as a different EMM.
A note about logging in sasc-ng:
If you'd like to play with this some more, grab sasc-ng R168/R114.
once it is running you should be able to do:
telnet localhost 5456
cam LOG on hexdata.emm,nagra.rawemm
sasc-ng will now print out the encrypted and decrypted EMMs. You can also do:
telnet localhost 5456
cam LOGCFG
which will display (in sasc-ng not in the telnet terminal) a list of all available logging commands that you can enable or disable.
What is assembly language
NOTE: I will use 'ST7OP' to refer to the 'ST7 Opcodes.pdf' document
If you don't know how to read hexadecimal or the basics of computer program, you probably need to do a bit more homework before coming back to this thread. I can't go down to that level. However assembly language is a (sort of) human readable representation of the actual commands executed by the host processor.
Each processor has an instruction set. This is a list of all possible commands that the processor can execute. Each one of these is represented by one (or several) bytes, and when many of these instructions are strung together, you get a program. Each instruction can be represented as a hexadecimal number, and each of these instructions has a human-readable equivalent.
time for a simple example:
a = 10;
while(a != 0) {a--;}
this will set 'a' to 10 then continuously decrement it until it reaches 0
To implement this in assembly (we'll do it for the ST7 processor since that's what we'll be working with later) you might use:
ld a, #$0a Load 10 (0a in hex) into 'a')
loop: define an address, has no impact on the program
dec a Decrement (subtract 1 from) 'a'
jrne loop Jump to 'loop' if 'a' is not equal to 0
don't worry too much about the notation above. We'll get to that, but it is worth explaining the #$10. '#' indicates an immediate value. That means we are directly specifying the value to load. We could instead load 10 from another variable or memory address. the '$' notes that we are specifying the value as hexadecimal (as opposed to decimal)
And here is (one possible) hex representation of this command:
00:0081 a6 0a ld a, #$0a
00:0083 4a dec a
00:0084 26 fd jrne $83
The 1st column represents the address in memory where the instruction is stored, next are he hexadecimal bytes that make up the instruction, and last is the relevant assembly code. This is the format that you'll get out of most disassemblers.
Registers: (see ST7OP page 6)
Each processor has a number of registers. In general these are used to execute arithmetic functions. For the ST7 there are 6:
A, X, Y: These can be used by many operations. A is the accumulator and is the only truely general-purpose register. X and Y are index registers (used for indexing functions, see below), but can also be sued as temporary storage.
PC: The program counter. This contains the memory address of the next instruction to execute.
SP: Stack pointer. See below where I talk about the stack a bit
CC: Conditional Code Register. This contains status information about recently executed commands.
About addressing modes: (see ST7OP page 8)
There is a lot to learn about Assembly language, and I don't have time to teach it all. You will need to know some common addressing modes though.
Immediate: A value is specified directly (like #$0a above)
Direct: A memory address is specified. Load the value from there
Indexed: add an immediate value to the contents of another register, and load the value from the resulting memory address
Indirect: add the value from the memory address given to the contents of another register, and load the value from the resulting memory address
Relative: Only used for jumps. This specifies that we add the value to the PC and use that as the next address to execute.
Now let's look at the assembly code above again:
ld a, $#0a <- uses immediate addressing to load the value $0a into the accumulator. PC is now $83
dec a <- subtracts one from the accumulator. When the value reaches zero, the 'Z' bit in the CCR is set. PC is now $84
jrne $83 <- check the Z bit of the CCR. If it is NOT set, then jump relative to $83. PC is now $86. Note that the opcode is '26fd'. '26' is the command for jrne'. 'fd' is the relative address to jump to. fd in hex is -3 (8bit signed) so we add $86 + (-3) and end up with $83 as the address to jump to.
Stack Pointer:
With only 3 registers available for arithmetic functions, we need more room to store variables. We can directly store them in RAM, but that only works for simple cases, since we need to know ahead of time where in RAM to put them. The stack-pointer contains a RAM address which can be used to store a variable. It must be initialized on startup (in the case of the ST7 family, it is initialized to the last byte of available RAM). a 'push' instruction pushes the value of a register into the address specified by the SP, then decrements the SP (so it points at a new available memory location). A 'pop' instruction increments the SP and retrieves the value specified by the SP into the specified register.
Homework:
Since we haven't talked much about the ST7/ST19, these are very simple examples. We'll see more as things progress.
1) Run this code through the disassembler, and try to tell me what it does:
NOTE: There was a bug in the original code. Make sure you are using the following code:
A60588855A894285A30126F8
2) write assembly code using ST7OP (give both the instruction and hexadecimal opcode) for the following:
a=0
x = 1;
while(a<512) {x = (x * 2) % 256;a = a + 1;}
Hint: you can ignore the '%256' It only means that we only care about the lowest 8 bits of X .
If you are familiar with Assembly code, please don't post the answers right away (or just PM them to me if you want me to check). I would rather those who aren't as familiar with assembly try to work this out. If you'd like to post answers, please post them in the 'homework assignment' thread so others can work them out on their own (please add the assignment number in your post to make it obvious which one you are posting about since there will be more to come). Feel free to ask questions here.
Update:
I had a question on using pdisassembly and why the code doesn't match what is in the ST7 manual.
There are several notations which are used interchangeably when looking at ST7 code, and you'll need to be able to recognize all of them.
The common ones are as follows:
bne == jrne
bz == beq == jrz == jreq
(there are several in the same category in the conditional branch case)
ld a, x == txa
ld x, a == tax
(NOTE: the 'ld' is actually poor terminology for this set of commands, but we'll get to that later)
call == callr == jsr (sort of I'm merging relative and absolute adressing here)
j == jp == jra == bra (again I'm merging addressing modes)
cp == cmp
Lesson 2: ROM 102
About the ROM102
The ROM102 uses an ST19XL processor (which is an enhanced version of the ST7 you have documentation for). The processor is based on a Motorola 6805 instruction set.
Specs:
4KB or RAM
18kB of EEPROM (electronically erasable/programmable ROM)
96kB of ROM
Because the address space is only 16bit (65kB), the ROM102 introduces a segmented (often called paged) memory layout. You'll see memory addresses like '00:8397' Here '00' is the page and '8397' is the address on that page.
The memory layout is as follows:
0000-0FFF : RAM
3000-307F : OTPROM
3080-37FF : EEPROM
4000-7FFF : UROM
00:8000-FFFF : ROM
01:8000-FFFF : ROM
02:8000-BFFF : ROM
80:8000-BFFF : EEPROM
Note that segments only apply to memory > 8000. You can access memory in the 0000-7FFF range from within any segment. More on segments below.
Also, there are some holes in there. specifically between 1000-2FFF and 3800-3FFF. I have heard that the MAPROM lives in some of that area, but regardless, it is protected and there is no easy way to see what is in there. For our uses, we'll just assume it doesn't exist.
RAM:
Different parts of RAM have different uses, but the important things to know for now is that 00-1f are unaccessible to us, and 0EF8-0FFF are normally reserved for the stack. The processor will also use 0DF8-0EF7 for storing I/O so you might be interested in snooping this at some point, but we can talk about that when playing with the simulator if needed.
OTPROM:
The OTPROM is one-time-programmable. That means that the card was initialized at the factory with all 0's in this area, and during programing some bits have been set to 1. Once set to '1' the bit can never be flipped back to '0'. This area is not very relevant to our endeavors, but if you ever hear about a 'marked' card, it means that one of these OTP bits was set, indicating that the card was detected running something it shouldn't. A marked card is a candidate for a loop ECM, but again, that doesn't really apply to us.
EEPROM:
The EEPROM can be programmed (and erased) on the fly, and there are specific commands which write to it. This is how the provider can change the programming and functionality of the card (and how they cause us all of our issues)
UROM:
The UROM is where the core code for the card is stored. All commonly used routines can be found here. Note that the ROM can never be changed once the card leaves the factory. The providers have foreseen the need to patch the ROM functions, and so some of the routines in the ROM can be superseded by code within the EEPROM (more on that later). The UROM is accessible from all memory segments.
Paged (segmented) memory:
There are 3 pages of ROM and 1 page of EEPROM. The 00 page contains more (slightly less used) functions needed to run the card. the 01 and 02 pages seem to contain random data and to be unused. The 80 page is where EEPROM lives, and this gets updated whenever the provider decides to do so. To access segmented memory, 2 new registers were introduced: The CSR and DSR. the CSR is the Code Segment Register, and the DSR is the Data Segment Resgister. You can expect that these registers will hold one of the following values:00, 01, 02, or 80. The DSR is set and read using load commands. When using a 'load' to retrieve a value from memory, the DSR is used to determine which page to load the data from. The CSR is used to determine where the next instruction will be read from (it is like an extension to the Program Counter). It can be read, but not written directly. Instead, it is set when a 'call' (often known as jsr or jump to subroutine) is executed. I don't believe there is any other way to change the CSR.
So to make more progress, we need to know a little more about assembly language.
We've talked about Load operations, but there is also an operation known as 'Transfer' The Transfer operation copies data from one register to another (i.e. directly from a to x). These are (confusingly) listed in ST7OP page 56 as 'Load' instructions, and it is ok to think of them like that, but there is a caveat to the 'Transfer' instructions: Unlike loads, they do not affect the CCR. You will often see transfer instructions listed as either 'TAX' (transfer A to X) or 'ld x, a' (load x with the value of a). These are equivalent. I prefer the 'tax' notation since it makes clear that the operation works differently (if only slightly) from a load.
Above I also mention CALL/JSR routines. There are 2 main ways to change where the program is executing:
Jump: This just changes the PC and the code proceeds on its way (at the new location)
JSR/RET: This pushes the current value of the PC onto the stack before jumping to the specified address (actually it pushes the address of the following instruction onto the stack). Then when a RET (return) is called, the original PC is popped off the stack and reloaded into the PC and the processor continues where it left off (just after the JSR routine)
As mentioned previously, there are different addressing modes. Both JSR and JMP can use either relative or absolute addressing. Relative addressing is useful for jumping within +/- 127 bytes of the current PC. Absolute addressing allows for jumping anywhere in memory.
There is also a special 'PJSR/PRET' set of commands. These are 'Paged Jump to Subroutine' and 'Paged Return' They allow specifying the page (segment) number along with the address thus allowing to jump from one page to another. To my knowledge there is no 'Paged Jump' instruction, so when jumping between pages, a PJSR is the only way to do it.
A quick note here:
JSR and CALL are the same. I will often use JSR though the ST7 manual uses 'CALL'
ST7OP distinguishes between CALL and CALLR. I will use JSR for either one. The only difference is the addressing mode
ST7OP uses JP and JRA. Again, these are identical except for the addressing mode, and I'll usually use 'JP'. You may see 'BRA' (branch) or 'J' (Jump) which is the same thing.
You may see CALLP or ECALL somewhere (it's an instruction not in ST7OP). This is equivalent to what I call JSRP or PJSR. They're all the same thing.
MAP functions
The MAP functions are mostly cryptographic functions. They are in a part of the ROM which is very difficult to get at, so it isn't really possible to read them from the ROM to disassemble and understand them (though there are claims this has actually been done). MAP functions are called with a specific set of instructions:
A6 44 ld a, #$44
8D 00 A8 22 jsrp #$00, $A822
ld a,#$44 loads 0x44 into 'A'. this is the map function to execute (MAP44 in this case). JSRP #$00, $A822 jumps to the MAP handler. This lives in rom at 00:A822. Each MAP function expects input data to be placed at a specific point in RAM and the output will be placed at a specific point in RAM (sometime overwriting the input)
We know what a lot of these MAP functions do already. You can find a list of known MAP functions in 'The Nagravision2 FAQ v1.02' on Page 2. Note that we've recovered the Map3B and Map57 although it isn't straight-forward to define their purpose.
Bug Tables
It is important before digging into more examples to understand bug tables. The providers realized that they might have bugs in their code (or the need to modify how code works) and so they implemented what is known as a bug-table. Basically the Bug-Table is a lookup table that allows certain functions that are implemented in ROM to be overridden by code in EEPROM (effectively replacing the ROM code with some EEPROM code). We'll need to get into how this works later, but for now you should be able to recognize code that has a hook.
Here is an instruction from the UROM:
8D 00 5F 92 ecall #0, BUG_CATCHER ; See if there is a PATCH for this routine
the code at 5F92 (again, note that ecall is the same a pjsr, you'll need to get used to having multiple names for the same instruction) does some trickery to compare the PC which was pushed on the stack with a list of values in EEPROM. If a match is found, the call will rediretc to EEPROM and the original routine will never be executed. Not every ROM function has a hook for a bug-table though. Since we're not going to be writing a lot of code for the ST19 we don't really care how to implement a Bug or which functions can support them. We just need to know how to check whether there is an EEPROM replacement function or not so we disassemble the right thing. But we'll get to that.
Homework:
3) What is the value of 'a' when the program counter reaches 008a:
$0080=A61097AD075A26FB9D9D9D9DCD00924C
$0090=819D4C90859085810000000000000000
0080: A6 10 ld a, $#10
0082: 97 tax
0083: AD 07 jsr $8c
0085: 5A dec x
0086: 26 FB jrne $83
0088: 9D nop
0089: 9D nop
008a: 9D nop
008b: 9D nop
008c: CD 00 92 jsr $92
008f: 4C inc a
0090: 81 ret
0091: 9D nop
0092: 4C inc a
0093: 90 85 pop y
0095: 90 85 pop y
0097: 81 ret
Some of you may want to use a simulator for this. That's ok (though It'd probably be better practice to work it out by hand), but make sure you understand WHY you get the answer you do.
Lesson 3: Using the simulator, and tracing some code
Using the simulator and analyzing some code.
Unfortunately I've been unable to get the simulator to run sufficiently on Wine, so you'll need a Windows system for this lesson.
To look at more complicated examples, we will learn to use the simulator now. The simulator allows us to load a ROM image, apply patches, and execute code. Since we haven't looked at EMMs yet, we're just going to learn to use the simulator for simple examples, and won't be running real commands through it yet.
Start up the Simulator. select 'Rom102.bin' as the rom and 'R102Rev10B.bn102' as the EEPROM
The main window can be used to send commands to the simulator, but they must be in a specific format, and we're not quite ready for that yet.
Instead, select Debug->Show Debug Window
You are now looking at the contents of RAM. You can see the values of various registers on the left.
We'll now load homework assignment (1) into RAM:
Select Memory->Patch Memory
In the new window enter:
$0080=A60588855A894285A30126F800000000
and press 'Apply patch'
note that I added a bunch of 0's at the end. The patch format which is used is:
$<addr>=<16bytes of data>
OR
$<addr><n (in hex)><n bytes of data>
So we could also have specified:
$0080CA60588855A894285A30126F8
You can see the changed bits in the RAM page now.
Next, click on the 'PC' (on the left) and type in '0080'
press 'F5', or the 'Step cycle by Cycle' button once
you'll see that the disassembled code appears in the bottom pane, that the accumulator (A register) has changed to 5, and the the PC has changed to 0082
This behavior is different than what you'll see if you ever use gdb in that gdb tells you the upcoming instruction when it stops, not the one it just executed. That is usually more useful, and my preference, but we use what we've got.
You can continue to pree F5 and step through the results until the program counter reaches '8c' and the program is done.
Now, let's look at something more interesting. We're going to use the simulator to watch the bug-table work.
Rather than executing a command, and entering the bug-table the normal way, we're going to jump right to the entry point. Basically, we'll load the EMM command into A, and starting code execution at the right point such that we execute the EMM processing bug.
before starting, make sure you have the ROM102 image loaded, and are using to 10B EEPROM image.
press 'reset' (F2) to clear out any current state.
click on 'A' and enter E3 (this is an E3 EMM which is implemented via an EEPROM override. We won't get far enough to care about the value of E3, but if you want to continue tracing after the tutorial is done, it is a good value to use)
click on 'PC' and enter 9595 (this happens to be the instruction before a call to the bug-handler in the EMM processing code)
scroll down in the RAM window to the end (so you can see 0FFF). We want to watch the stack in this example
start stepping through the code.
Also, you might want to look at the 102 disassembly as well, as it is quite well documented for this function.
here's what you should see:
00:9595 8D 00 5F 92 XJSR #$00,$5F92 DSR=00 A=E3 X=00 Y=00 SP=0FFC CCR=??------
-> 5f92 is the address of the bug handler. Note that $0FFE:$0FFF contain 0x9599. This is the address we would return to if we called a 'ret' right now, and it is the address we're going to try to match in the bug-table.
<<TRAP>>
00:5F92 89 PUSH X DSR=00 A=E3 X=00 Y=00 SP=0FFB CCR=??------
-> save off x, a, cc. this is very common in subroutines
00:5F93 88 PUSH A DSR=00 A=E3 X=00 Y=00 SP=0FFA CCR=??------
00:5F94 8A PUSH CC DSR=00 A=E3 X=00 Y=00 SP=0FF9 CCR=??------
00:5F95 B6 05 LDA $05 DSR=00 A=00 X=00 Y=00 SP=0FF9 CCR=??----Z-
00:5F97 A4 9E AND #$9E DSR=00 A=00 X=00 Y=00 SP=0FF9 CCR=??----Z-
-> don't worry about this. Just checking some bits to see if the bug-handler is enabled.
00:5F99 27 04 BEQ $5F9F DSR=00 A=00 X=00 Y=00 SP=0FF9 CCR=??----Z-
00:5F9F C6 31 77 LDA $3177 DSR=00 A=AF X=00 Y=00 SP=0FF9 CCR=??---N--
00:5FA2 CE 31 76 LDX $3176 DSR=00 A=AF X=01 Y=00 SP=0FF9 CCR=??------
00:5FA5 27 03 BEQ $5FAA DSR=00 A=AF X=01 Y=00 SP=0FF9 CCR=??------
00:5FA7 C6 31 78 LDA $3178 DSR=00 A=B9 X=01 Y=00 SP=0FF9 CCR=??---N--
->$3176 is an EEPROM address. It controls whether we use the standard or alternate bug-table. for a given revision, this will be fixed, and in 10B 3176 contains '1'. '0' means we use the 'alt' table, anything else means we use the normal table.
$3178 contains the length of the standard bug-table (and $3177 contains the length of the alt bug-table). the table-length is 0xB9
00:5FAA 88 PUSH A DSR=00 A=B9 X=01 Y=00 SP=0FF8 CCR=??---N--
00:5FAB C6 30 58 LDA $3058 DSR=00 A=FF X=01 Y=00 SP=0FF8 CCR=??---N--
00:5FAE C4 30 5E AND $305E DSR=00 A=FF X=01 Y=00 SP=0FF8 CCR=??---N--
00:5FB1 4C INC A DSR=00 A=00 X=01 Y=00 SP=0FF8 CCR=??----Z-
00:5FB2 26 14 BNE $5FC8 DSR=00 A=00 X=01 Y=00 SP=0FF8 CCR=??----Z-
00:5FB4 27 6B BEQ $6021 DSR=00 A=00 X=01 Y=00 SP=0FF8 CCR=??----Z-
->There are 2 bug-catcher routines. They work basically the same way, but one computes checksums and the other doesn't. Unfortunately, we're using the checksum version which is a bit more complicated. The checksums are there (I think) to make it harder to hack the bug-table, so I'm going to ignore the checksum stuff mostly...
00:6021 AE FB LDX #$FB DSR=00 A=00 X=FB Y=00 SP=0FF8 CCR=??---N--
-> $FB is equal to -5. Each entry in the table is 5 bytes long.
00:6023 A6 00 LDA #$00 DSR=00 A=00 X=FB Y=00 SP=0FF8 CCR=??----Z-
00:6025 88 PUSH A DSR=00 A=00 X=FB Y=00 SP=0FF7 CCR=??----Z-
-> the value in 'a' is checksum related, we can ignore it
00:6026 20 16 BRA $603E DSR=00 A=00 X=FB Y=00 SP=0FF7 CCR=??----Z-
00:603E 31 E7 01 STA $01,SP DSR=00 A=00 X=FB Y=00 SP=0FF7 CCR=??----Z-
00:6041 9F TXA DSR=00 A=FB X=FB Y=00 SP=0FF7 CCR=??----Z-
00:6042 AB 05 ADD #$05 DSR=00 A=00 X=FB Y=00 SP=0FF7 CCR=??-H--ZC
-> A now contains the index of the next bug (note that 'a' is 0, so we're at the beginning now)
00:6044 97 TAX DSR=00 A=00 X=00 Y=00 SP=0FF7 CCR=??-H--ZC
00:6045 31 E3 02 CPX $02,SP DSR=00 A=00 X=00 Y=00 SP=0FF7 CCR=??-H---C
-> we're comparing 'x' to the value stored in $SP+2 ($0FF9) which is the length of the bug table that we stored earlier (0xB9)
00:6048 25 2E BCS $6078 DSR=00 A=00 X=00 Y=00 SP=0FF7 CCR=??-H---C
-> x < 0xb9, so we continue processing
00:6078 D6 31 7B LDA $317B,X DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??-H---C
00:607B 31 E1 08 CMP $08,SP DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??VH-N-C
00:607E 26 A8 BNE $6028 DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??VH-N-C
-> The bug table starts at $3179. it is composed of pairs of addresses as follows:
<address to override> <where to jump>
<address to override> is 3 bytes (CSR + 2 bytes for the address)
<where to jump> is 2 bytes (CSR must be 0x80 since we are patching into EEPROM, so we don't include it, just the actual address)
so we load the LSB (3179 + X (which is 0 here) + 2 (0 is CSR, 1 is most-significant-byte, 2 is least-significant byte), and compare it against the value in $SP+8 ($SP is 0xFF7 + 8 = $0FFF, and the value in memory there is 99..which is the LSB of the address we're trying to map). Here A contains 1A != 95 so we didn't match and we will try again.
00:607E 26 A8 BNE $6028 DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??VH-N-C
00:6028 31 E8 01 EOR $01,SP DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??VH---C
00:602B D8 31 79 EOR $3179,X DSR=00 A=1A X=00 Y=00 SP=0FF7 CCR=??VH---C
00:602E D8 31 7A EOR $317A,X DSR=00 A=9A X=00 Y=00 SP=0FF7 CCR=??VH-N-C
00:6031 20 0B BRA $603E DSR=00 A=9A X=00 Y=00 SP=0FF7 CCR=??VH-N-C
00:603E 31 E7 01 STA $01,SP DSR=00 A=9A X=00 Y=00 SP=0FF7 CCR=??-H-N-C
->This is all checksum related stuff, but we are now back at $603E and ready for another round. At this point it would be boring to press the stepping button until we found match, so let's set a breakpoint (or 2). Right click on the Breakpoint window, select 'Add Breakpoint, and enter '604A'. Repeat and enter 6080. The simulator will stop whenever the PC reaches a breakpoint.
If you look carefully at the trace so far, $604A is the address right after the compare between 'x' and 0xB9. If we reach this we have traversed the entire table and haven't found a match (there is no bug for this address).
$6080 is the address right after the compare between 'a' and 0x99 (the LSB of our address). If we get here, we have at least a partial match of the address.
Now press the 'start' button
the screen will scroll for a few seconds and end with:
00:607E 26 A8 BNE $6028 DSR=00 A=99 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
->the PC is at 6080 so we found a match for the LSB of the address. 'X' is at 0x32 (this is the current index) so the address we loaded was
$3179+2+$32 = $31AD Select View->EEPROM and scroll down to $31A0. We can see the $31AB:$31AD contains 00:9599 which is the address we're comparing against, so we found a match. The next 2 bytes are '81 E2' so we should expect that we'll jump to 80:81E2. We'll now follow the code and see if we're right. Use the single-step mode again.
00:6080 31 E8 01 EOR $01,SP DSR=00 A=50 X=32 Y=00 SP=0FF7 CCR=??-H----
00:6083 31 E7 01 STA $01,SP DSR=00 A=50 X=32 Y=00 SP=0FF7 CCR=??-H----
->This is more checksum stuff. just ignore it.
00:6086 D6 31 7A LDA $317A,X DSR=00 A=95 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:6089 2A E6 BPL $6071 DSR=00 A=95 X=32 Y=00 SP=0FF7 CCR=??-H-N--
->This is checking whether the MSB of the address is less than 0x80. If it is, we can ignore the CSR byte (remember that all addresses from $0000-$7FFF are accessible from any segment). In this case the MSB is 0x95, so we can't ignore the CSR byte.
00:608B 31 E1 07 CMP $07,SP DSR=00 A=95 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
00:608E 26 A3 BNE $6033 DSR=00 A=95 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
->check whether the MSB matches our address (it does)
00:6090 31 E8 01 EOR $01,SP DSR=00 A=C5 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:6093 31 E7 01 STA $01,SP DSR=00 A=C5 X=32 Y=00 SP=0FF7 CCR=??-H-N--
->more checksum stuff
00:6096 D6 31 79 LDA $3179,X DSR=00 A=00 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
00:6099 31 E1 06 CMP $06,SP DSR=00 A=00 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
00:609C 26 9D BNE $603B DSR=00 A=00 X=32 Y=00 SP=0FF7 CCR=??-H--Z-
->check whether the CSR byte matches our address (it does)
00:609E 31 E6 04 LDA $04,SP DSR=00 A=E3 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60A1 31 E7 02 STA $02,SP DSR=00 A=E3 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60A4 31 E6 03 LDA $03,SP DSR=00 A=C0 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60A7 31 E7 01 STA $01,SP DSR=00 A=C0 X=32 Y=00 SP=0FF7 CCR=??-H-N--
->remember how we pushed x, a, cc when we entered the bug handler? We need to get them back so they're available to the bug routine. This has just loaded those values from deep in the stack and pushed duplicates on.
00:60AA D6 31 7C LDA $317C,X DSR=00 A=81 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60AD 31 E7 04 STA $04,SP DSR=00 A=81 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60B0 2B 11 BMI $60C3 DSR=00 A=81 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60C3 A6 80 LDA #$80 DSR=00 A=80 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60C5 31 E7 03 STA $03,SP DSR=00 A=80 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60C8 D6 31 7D LDA $317D,X DSR=00 A=E2 X=32 Y=00 SP=0FF7 CCR=??-H-N--
00:60CB 31 EE 05 LDX $05,SP DSR=00 A=E2 X=00 Y=00 SP=0FF7 CCR=??-H--Z-
00:60CE 31 E7 05 STA $05,SP DSR=00 A=E2 X=00 Y=00 SP=0FF7 CCR=??-H-N--
->And now we've placed 80:81E2 onto the stack, and loaded the orginal 'x' into itself.
00:60D1 86 POP CC DSR=00 A=E2 X=00 Y=00 SP=0FF8 CCR=??------
00:60D2 84 POP A DSR=00 A=E3 X=00 Y=00 SP=0FF9 CCR=??------
->Pop A and CC, and if you look at the SP you'll see that 0xFFC contains 80:81E2
00:60D3 87 XRTS DSR=00 A=E3 X=00 Y=00 SP=0FFC CCR=??------
->we execute a 'ret' and the PC is now at 80:81E2 and we're running the override function. Note that the original address 00:9599 is still on the stack. The bug routine could play a trick like I used in Lesson 2 to not return to 00:9599, or it could terminate normally and allow the code at 00:9599 to continue (i.e. we can either replace the function with the bug code, or we can pre-process data in the bug-code and then execute the original function when we're done
The only homework for this assignment is to try to understand what we went through here. Remember to use the Annotated ROM102 disassembly if you are confused. This is quite a bit more complicated than what we've done so far, but it is important that you understand what we did so you'll have a chance to understand what code is doing when you don't have any help (for instance the code at 80:81E2 is undocumented, and you'd need to figure out how it works without the help of the annotated Rom102 Disassembly).
Lesson 4
----------
EMMs and ECMs:
Note: I'll refer to the Nagra2 Echostar FAQ v0.3.txt in this lesson, which I will abbreviate FAQ from now on.
A few definitions:
Card Revision: This is the version of the EEPROM code currently loaded on the card. In the past (aside from the keys) EEPROM was usually only changed by a Revision update. Currently DN is using Rev10C and BEV is using Rev248. These are virtually equivalent.
EMM is an 'Entitlement Management Message' These can be used to determine whether a card is allowed to decrypt a given stream. For our purposes, the EMM is used to either update EEPROM or to change the decryption keys.
ECM is an 'Entitlement Control Message' This is used to generate valid CW keys (see below)
You'll also hear about ECM as 'Electronic Counter Measure'. This is when the provider sends a set of commands to try to kill hacked cards. They occasionally find a way to execute code that executes differently on a hacked card than a legally subscribed card, and they'll use this discrepancy to set the card to run in an infinite loop such that it (usually) can't be recovered. We're not going to talk about these.
There is a large list of EMMs (which can be found in FAQ), but we're really only interested in a few:
B1: the payload will be stored in RAM (at 0093) and will be executed as code. In the past a B1 was used to update EEPROM for new revisions. Recently the B1 has been used to update the 00/01 decryption keys
E3: this is a newish EMM which is processed in the bug-handler. It is an efficient way to update EEPROM. The payload contains memory-addresses and data to be loaded at that address (much like applying a patch as described in lesson 3).
E0: the EMM E0 is used in some cases to update the 00 or 01 decryption keys.
42: Also known as DT06, this EMM updates the 00 or 01 decryption keys
9F: I haven't looked at it yet. This is something new in Rev10C/Rev249. It is part of what caused all of the breakage.
High-level analysis of decrypting a channel:
The raw stream data (the video and sometimes the audio) are encrypted using an algorithm called CSA. It is a standard algorithm for all DVB providers (it has nothing to do with Nagra2), and some DVB cards (like the Nexus) have hardware to decrypt the stream (for budget cards we use FFdeCSA for decrypting this). To run the CSA algorithm, a key (called the codeword or CW) is needed, and the reason you are reading all this is to figure out how to get the correct CW. Currently, CWs are valid for 30 seconds each. There is a pair of them (noted as odd and even) and they change alternately every 15 seconds. The odd key is changed while the even one is in use, and vice-versa so a valid key is always available (so the stream switches back and forth using the odd and even keys such that a valid key is always in use.
The CWs themselves are also encrypted (and this is where Nagra2 comes into play).
The CW update is sent in an ECM. The key needs to be decrypted using either the 00 or 01 key and (usually) the M1 key. In recent revisions, the result is then morphed using some MAP functions to generate a correct CW, and in the very last revision (Rev148/Rev10C) the raw data is morphed prior to running the decrypt as well.
The 00 and 01 keys change less frequently, and recently this has been done using B1 EMMs. The B1 is normally a 'morph' which manipulates the current EMM into a different one (via code and/or calls to ROM/EEPROM) becoming an E0 or 42 EMM. This morphed EMM is executed as soon as the B1 processing is complete, and actually updates the 00 or 01 keys
Sending a command to the simulator
The smartcard has an interface by which it sends and receives commands. We can't just take the raw stream from the satellite and pass it to the card, but instead need to format it 1st.
The format of the data is as follows:
<header><length><data><end><lrc>
I'm not sure all the possible values of the <header>, but we can use '21 40 06 DA CA 00 00' for our purposes
<length> is 1 byte and is the length of the data
<end> is 02
<lrc> is a checksum. There is no reason to know how to calculate it here. The simulator can calculate it for us.
the data is further divided as:
<cmd><len1><id><packet>
<cmd> is the packet type. There is a list of them in the Nagra2 Echostar FAQ 0.3 (from now on abbreviated as FAQ) starting on line 312. Since we'll be focusing on EMM commands, we'll expect this to be 04
<len1> is the length of the current packet. it is possible to store multiple packets inside a single data, but I don't believe any providers do so. In this case len1 is always length-2
<id> is 2 bytes and is the provider id. This defines which keys to use to decrypt the packet.
<packet> is an encrypted packet to process. It must be decrypted (by the smartcard) before it can be used.
Here is a raw EMM I captured using vdr-sc:
[hexdata.emm] EMM pid 0x0120
[hexdata.emm] 0000: 82 70 6c 00 00 00 00 00 04 65 00 01 82 00 10 92
[hexdata.emm] 0010: 02 b6 f1 8f cb 93 0a 47 76 77 36 07 99 eb 74 8c
[hexdata.emm] 0020: 8f 30 77 02 f9 c3 a0 3f c0 66 3e f9 90 27 e1 8d
[hexdata.emm] 0030: 04 14 cf 22 de 31 2a e6 f7 fe ba 35 1a c8 5f 7f
[hexdata.emm] 0040: 0c 2e b5 eb ff 64 bc b0 07 55 80 3b f4 72 c9 09
[hexdata.emm] 0050: 88 0d df 3e 5b 8f 48 5d 41 18 ea 84 4b 3a c6 a0
[hexdata.emm] 0060: 7f 2f f0 eb df 0f 4b 91 e9 a9 c1 11 c0 22 52
the <header>, <length>,<end>, and <lrc> are not present in the packet.
the <data> starts at 0008 for an EMM packet
so putting it all together:
<header>21 40 06 DA CA 00 00
<length>67 (remember this is len1+2 and len1 is 65 (see below)
<data>04 65 00 01 82 00 10 92
02 b6 f1 8f cb 93 0a 47 76 77 36 07 99 eb 74 8c
8f 30 77 02 f9 c3 a0 3f c0 66 3e f9 90 27 e1 8d
04 14 cf 22 de 31 2a e6 f7 fe ba 35 1a c8 5f 7f
0c 2e b5 eb ff 64 bc b0 07 55 80 3b f4 72 c9 09
88 0d df 3e 5b 8f 48 5d 41 18 ea 84 4b 3a c6 a0
7f 2f f0 eb df 0f 4b 91 e9 a9 c1 11 c0 22 52
(note that <cmd> is 04, <len1> is 65, <id> is 00 01)
<end>02
<lrc>we don't care, let the simulator compute it
To test it, do the following:
Close the Debug window if you have it open
Make sure you have the Rom102.bin and 10B EEPRM loaded.
Press 'Reset'
Press 'Start'
Select 'Add LRC'
Paste this into the command window:
21406Da0ca000067
046500018200109202b6f18fcb930a477677360799eb748c8f 307702f9c3a03fc0663ef99027e18d0414cf22de312ae6f7fe ba351ac85f7f0c2eb5ebff64bcb00755803bf472c909880ddf 3e5b8f485d4118ea844b3ac6a07f2ff0ebdf0f4b91e9a9c111 c02252
02
Press 'Send'
You should see:
> 21 40 6D A0 CA 00 00 67 04 65 00 01 82 00 10 92 02 B6 F1 8F CB 93 0A 47 76 77 36 07 99 EB 74 8C 8F 30 77 02 F9 C3 A0 3F C0 66 3E F9 90 27 E1 8D 04 14 CF 22 DE 31 2A E6 F7 FE BA 35 1A C8 5F 7F 0C 2E B5 EB FF 64 BC B0 07 55 80 3B F4 72 C9 09 88 0D DF 3E 5B 8F 48 5D 41 18 EA 84 4B 3A C6 A0 7F 2F F0 EB DF 0F 4B 91 E9 A9 C1 11 C0 22 52 02 D9
Performing EEPROM Clean-up...
EEPROM Clean-up Done.
MapFunc $02
MapFunc $07
MapFunc $04
MapFunc $46
MapFunc $0B
Export = 27F9071D67A6FD9CB02B4DC4449467D794E5FA9B0F0D19DE76 75AE09035AEA8287047975C54380E0F73CFC399E64513D0313 FF9CFC46B494E9EEBEAC18A144BAA5736867D8B48A1409AC04 F34DE640127CD32C43B3C64EC97E149F0DC02944A5
MapFunc $02
MapFunc $49
MapFunc $48
MapFunc $0D
MapFunc $0C
MapFunc $02
MapFunc $07
MapFunc $04
MapFunc $3C
MapFunc $0B
Export = 2F84E5DEB8696876000113DB0001171A7789B1A8009D71804E 4E010142BCBCE2DE963F6891178251194A62D404904B8A8E2D CB35A072D7AAB98AD983AE1C90934E4ED68127A8008D008172 4E4AE89FE79F5A2AEA4E4E4E4E7100A60DCC956981
< 12 40 04 84 00 90 00 42
Now, press 'Stop'
This means the emm was processed (whether it passed or failed is specified in the result value, and I'm not going to worry about that right now. The value under the 2nd 'Export' is the decrypted EMM. You can see that it matches the one I retrieved from vdr-sc:
[nagra.rawemm] Nagra2 RAWEMM
[nagra.rawemm] 0000: 2f 84 e5 de b8 69 68 76 00 01 13 db 00 01 17 1a
[nagra.rawemm] 0010: 77 89 b1 a8 00 9d 71 80 4e 4e 01 01 42 bc bc e2
[nagra.rawemm] 0020: de 96 3f 68 91 17 82 51 19 4a 62 d4 04 90 4b 8a
[nagra.rawemm] 0030: 8e 2d cb 35 a0 72 d7 aa b9 8a d9 83 ae 1c 90 93
[nagra.rawemm] 0040: 4e 4e d6 81 27 a8 00 8d 00 81 72 4e 4a e8 9f e7
[nagra.rawemm] 0050: 9f 5a 2a ea 4e 4e 4e 4e 71 00 a6 0d cc 95 69 81
So, let's rerun the same EMM, but add a breakpoint in the EMM processing code.
do another 'Reset'
Select 'Add Breakpoint' and add 00:9593 as well as 00:959B (9593 is right at the beginning of the EMM processor (just before the bug-handler call), and 00:959B is right after the bug-handler returns (we can't use 00:9599 because the simulator won't honor a breakpoint at a RET location)
Start the simulator, and paste in the same command we pasted before (make sure the 'add lrc' is selected
It should pop open the debug window after a few seconds.
The PC should be 9593, and 'A' should contain B1. we know from lesson 3 that 'A' will contain the EMM type at this point, so this must be a B1 EMM. Take a look at the RAM window between 0080 and 00E0. Notice that it looks very similar to the decrypted EMM above (in fact it is identical except for the first 5 bytes. If we wanted to, we could install a decrypted EMM here and start processing from 00:958F, and skipp all the header and decryption stuff. But let's continue. I happen to know that a B1 isn't handled by the bug-handler, so close the debug window. The debug window should pop back open shortly with the PC set at 959B. We have now passed through the bug-handler, and now we'll see how the B1 gets handled.
You should see on your screen:
80:80E1 87 XRTS DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H-N-C
<<EMMCMD01>>
00:9599 A1 01 CMP #$01 DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H-N--
-> Now that is helpful. The simulator identified the EMM-01 processing code. Well, we're looking for the B1 handler, so just step through until you see:
<<EMMCMDB1>>
00:A1B8 A1 B1 CMP #$B1 DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H--Z-
-> We've found the B1 handler. So let's see what it does.
00:A1BA 26 12 BNE $A1CE DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H--Z-
00:A1BC 0B 62 0C BRCLR5 $62,$A1CB DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H--ZC
00:A1BF 1C 61 BSET6 $61 DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H--ZC
00:A1C1 18 64 BSET4 $64 DSR=00 A=B1 X=00 Y=97 SP=0FF9 CCR=??-H--ZC
00:A1C3 BD 93 JSR $93 DSR=00 A=B1 X=00 Y=97 SP=0FF7 CCR=??-H--ZC
00:0093 A8 00 EOR #$00 DSR=00 A=B1 X=00 Y=97 SP=0FF7 CCR=??-H-N-C
-> So basically it does a check on memory in $62 (we haven't seen the BRCLR command before, but this checks whether bit 5 of the value in $62 is 0 and jumps to A1CB if so (it wasn't so we didn't), then sets a few status bits (BSET just sets a specific bit of a specific memory location) and then it jumps into RAM at $93 (which is the byte immediately following the 'B1' in the EMM.
There isn't too much point in following the B1 through, since it just does a lot of random stuff to morph itself. The key is that it will eventually jump back to $9569 and reprocess itself as a different EMM.
A note about logging in sasc-ng:
If you'd like to play with this some more, grab sasc-ng R168/R114.
once it is running you should be able to do:
telnet localhost 5456
cam LOG on hexdata.emm,nagra.rawemm
sasc-ng will now print out the encrypted and decrypted EMMs. You can also do:
telnet localhost 5456
cam LOGCFG
which will display (in sasc-ng not in the telnet terminal) a list of all available logging commands that you can enable or disable.