Basic TVD cracking

Hits: 9673

Author: Wayne Kerr
Submitted by: WayneK
Date: 2004-05-19 04:49
No tags

Amiga cracking - A look at basic TVD's

[0] Introduction
[1] A first look at a real TVD
[2] So, what now?
[3] Final words

.-=[ [0] Introduction ]=-.

TVD is an acronym for "Trace-Vector Decoder", which is a commonly-used method of protecting code/data as part of a copy-protection routine. It takes its name from the fact that it uses the 68000 TRACE exception vector to decode
something (typically the next instruction). A TRACE exception occurs when the trace bit in the status register is set to 1.
[ pic: 68000 STATUS REGISTER]

When the trace bit is set, execution jumps to the address stored @ 00000024 (the offset in the 68000 vector table for the TRACE vector). As with other exceptions, the current SR value (16 bits) and the return address (32 bits) are
pushed onto the stack. When the exception finishes (with an RTE instruction), these values are used to continue execution as before - obviously this means that unless something else changes the trace bit in the status register, the trace vector will be called after every single instruction (since the trace bit had to be set to get into the trace vector routine, and it's restored when
we leave it!). And that's the key to TVD routines - they typically decode the next instruction to be executed, and once that decoded instruction has completed, they decode the next one, and so on...So, now that you (hopefully) have some idea what this is all about, let's dive in and have...
.-=[ [1] A first look at a real TVD ]=-.

This example was found in the game: ALTERED BEAST (C) ACTIVISION/SEGA. You should have the original disk or a copy of the CAPS .ipf file(s) if you want to take a look at it for yourself. It's an early Rob Northen "Copylock" protection that returns the "magic number" in D0 and the trace vector (take a look at the contents of 00000024 after the drive-grinding Track 0 check!).

The TVD in this game (as in most/all of Rob's protections) is used to protect the disk-protection check proper (Track 0 has a different sync, data is read and a simple checksum routine is performed on the returned data to calculate the "magic number" for the game). Right, power up the Amiga+AR (or your personal choice of monitor cart/software (or even WinUAE!)), and let's have a look at it!
(All code is in UPPERCASE, comments are after the ";" character)
First, we have the setup:

0171E0 : LEA 17260,A0 ; 17260 is written into the vector table @ 00000010, this is the ILLEGAL;INSTRUCTION vector, which is jumped to if the 68000 can't make sense of the;current opcode, or if it's deliberately called with the 'ILLEGAL' instruction;when we reach this code, we are inside an ILLEGAL EXCEPTION
0171E4 : MOVE.L A0, 10
017260 : MOVEM.L D0/A0-A1,-(A7)
017264 : LEA 1729A,A0 ;TRACE VECTOR = 1729A (this is the decoder)
017268 : MOVE.L A0, 24
01726E : LEA 176C4,A0 ;PRIVV VECTOR = 176C4(called when a PRIVILEGE VIOLATIONoccurs;when the cpu tries to use an instruction in user mode which isn't allowed.;In this game 176C4 contains an RTS instruction, and is used to exit the;protection check and return to the game code...
017272 : MOVE.L A0, 20
017278 : ADDI.L #2, E(A7) ;Here is the "clever" bit... the ADDI instruction adds 2 to the return address;stored on the stack, the ORI instruction sets the interrupt mask in the saved;SR value on the stack (changes it from 2000 to 2700, turning off all irqs;except level 7), the BCHG instruction flips bit 7 in the saved SR high-byte;value. This will set the trace bit when this value is retrieved (when the next;RTE instruction is executed), thereby jumping to the TVD!
017280 : ORI.B #7, C(A7)
017286 : BCHG #7, C(A7)
01728C : LEA 171A2,A1 ;if the BCHG instruction @ 017286 set the zero flag, we jump into the TVD;halfway through (used to turn OFF the TVD later)
017290 : BEQ.S 172AC
017292 : MOVEA.L (A1),A0
017294 : MOVE.L 4(A1),(A0)
017298 : BRA.S 172C0 ;jumps to the end of the TVD, which does an RTE, and starts the TVD :)
;*** trace vector decoder begins ***
01729A : ANDI # F8FF,SR ;turn on interrupts by setting irq mask to 000
01729E : MOVEM.L D0/A0-A1,-(A7) ;save registers before use
0172A2 : LEA 171A2,A1
0172A6 : MOVEA.L (A1),A0 ;A0 now = the address of the instruction decoded in the previous TVD loop;this writes the previously used encrypted value over the old instruction.;This means every time the TVD runs, it encrypts the last instruction executed,;which of course means you can't let the TVD loop through a certain number of;times and dump the decrypted code from memory (Rob's not that stupid!)
0172A8 : MOVE.L 4(A1),(A0)
0172AC : MOVEA.L E(A7),A0 ;A0 now = the next instruction to be decoded (the instruction we return to when;the next RTE instruction occurs.
0172B0 : MOVE.L A0,(A1) ;save the address in memory for next loop;moves the encrypted value into memory for next loop (used to overwrite the;decoded instruction once it's been executed).
0172B2 : MOVE.L (A0),4(A1)
0172B6 : MOVE.L -4(A0),D0 ;get the encrypted dword from memory (4 bytes before the instruction we're about;to decode) into D0
0172BA : NOT.L D0 ;the extremely simple decryption routine.
0172BC : SWAP D0 ;the EOR writes the decoded instruction into (A0), which we are about to reach;when the RTE below is executed...
0172BE : EOR.L D0,(A0)
0172C0 : MOVEM.L (A7)+,D0/A0-A1 ;restore the registers
0172C4 : RTE ;return from exception.;remember that RTE has set the status register back to the saved value, which;means that the trace bit will still be set (unless cleared by another routine);therefore after executing the instruction that has just been decoded we'll be;back into the TVD!

Phew! A lot of commentary for such a little piece of code, but I hope it was useful... right about now, you're probably asking yourself
.-=[ [2] So, what now? ]=-.
Well, we obviously need to get to the unencrypted code somehow in order to see exactly what the protection is doing.
Let's have another quick look at the original TVD:
01729A : 027C F8FF ANDI # F8FF,SR
01729E : 48E7 80C0 MOVEM.L D0/A0-A1,-(A7)
0172A2 : 43FA FEFE LEA 171A2,A1
0172A6 : 2051 MOVEA.L (A1),A0
0172A8 : 20A9 0004 MOVE.L 4(A1),(A0)
0172AC : 206F 000E MOVEA.L E(A7),A0
0172B0 : 2288 MOVE.L A0,(A1)
0172B2 : 2350 0004 MOVE.L (A0),4(A1)
0172B6 : 2028 FFFC MOVE.L -4(A0),D0
0172BA : 4680 NOT.L D0
0172BC : 4840 SWAP D0
0172BE : B190 EOR.L D0,(A0)
0172C0 : 4CDF 0301 MOVEM.L (A7)+,D0/A0-A1
0172C4 : 4E73 RTE

Now, originally I replaced the last 2 instructions of the TVD with:
0172C0 : 4EF9 0000 00C4 JMP 000000C4

so that it would jump to my routine, however...after a few crashes it became obvious that something was going horribly wrong :) I suspected a checksum, but what I found (by a LOT of breakpoints/tracing, I won't bore you with all that) was quite clever... Rob uses the last 4 bytes of the TVD (half of the MOVEM and the 2 bytes for the RTE instruction) as one of his decryption key values - this way there's no need for a separate checksum routine, since if you modify them the instruction it decodes is going to be incorrect, and... well, you know the rest! This is not good, but now that we know that, we can move our hook up into the routine, and avoid altering the bytes that cause the crash:
0172B6 : 2028 FFFC MOVE.L -4(A0),D0
0172BA : 4680 NOT.L D0

now becomes:
0172B6 : 4EF9 0000 00C4 JMP 000000C4
We don't alter any other bytes in the original TVD. Now we need to insert the following code @ 000000C4:
0000C4 : 2028 FFFC MOVE.L -4(A0),D0
0000C8 : 4680 NOT.L D0
0000CA : 4840 SWAP D0
0000CC : B190 EOR.L D0,(A0)
0000CE : 2250 MOVEA.L (A0),A1
0000D0 : 2008 MOVE.L A0,D0
0000D2 : 0080 0060 0000 ORI.L # 600000,D0
0000D8 : 2040 MOVEA.L D0,A0
0000DA : 2089 MOVE.L A1,(A0)
0000DC : 06B9 0000 0001 0000 00C0 ADDI.L #1, C0
0000E6 : 4CDF 0301 MOVEM.L (A7)+,D0/A0-A1
0000EA : 4E73 RTE

I've written this here without comments so you can type the bytes into memory if you're following along step-by-step... here's the commented version:

0000C4 : 2028 FFFC MOVE.L -4(A0),D0 ;these are the remaining instructions from the original TVD, that we need to;execute before doing anything else.;Now we can use registers D0, A0 and A1 for our purposes, since we restore them ;at the end...
0000C8 : 4680 NOT.L D0
0000CA : 4840 SWAP D0
0000CC : B190 EOR.L D0,(A0)
0000CE : 2250 MOVEA.L (A0),A1 ;get decrypted instruction into A1
0000D0 : 2008 MOVE.L A0,D0 ;get address of instruction into D0
0000D2 : 0080 0060 0000 ORI.L # 600000,D0
;***Please note, this value may differ on your Amiga, change this if you do not ;have memory @ 600000 (in my case I have 1mb Fastram located here)
0000D8 : 2040 MOVEA.L D0,A0 ;A0 = new address to write opcode to
0000DA : 2089 MOVE.L A1,(A0) ;write decrypted opcode into our new 'high' address
0000DC : 06B9 0000 0001 0000 00C0 ADDI.L #1, C0 ;keep count of how many traces we've run
0000E6 : 4CDF 0301 MOVEM.L (A7)+,D0/A0-A1 ;these are the final 2 instructions
0000EA : 4E73 RTE from the original TVD that we must execute.

And that's it! Now when this has all executed the game should start, and when you look in memory @ 6171EC onwards you'll see the decrypted Rob Northen Copylock routine just sitting there, waiting for you to save out to disk and then load it into your favourite disassembler (ReSource, IRA, or even IDA on PC if you're not an Amiga purist!). Also, take a look at the trace counter we stored @ 000000C0, and think how long that would've taken to trace by hand :)

.-=[ [3] Final words ]=-.

Now, I know there was no point in decoding the protection routines in this particular game, since it performs a simple check and gives you the magic number in the trace vector handler address @ 00000024... however, it does illustrate the principles you'll need if you're going to crack harder TVD protections in future (be prepared for cartridge detection, checksums everywhere and worse!), so I hope you'll agree it was a worthwhile tutorial.
If there are any errors in the tutorial, forgive me, it's 05:40am and I'm starting to feel slightly tired! Thanks for reading, see you in a future tutorial (possibly!)...Zzzzz!
BTW: De La Soul obviously never cracked Altered Beast. The magic number
isn't 3, it's 664E90EA.

-Wayne Kerr, May 2004 (Yes, I know, 15 years late!)

Powered by the best online Amiga mod player: FLOD

Some more you may like:
Defjam - GFA BasicThe Band - Highsoft BasicSwiss Cracking AssocationStormlord Cracking Tutorial


Leave a Comment!

: Use this calculator
Your comment will be available for editing for 10 minutes
2016-07-31 20:47

1. scenex writes

I was wondering whether the last 4 bytes of the TVD are always used as a decryption key? Or is it dependent on the game/copylock generation?
2016-08-01 09:15

2. WayneK writes

I'm pretty sure it depends on the game/what age the copylock code is! I've only cracked a handful of them, maybe one of our resident copylock specialists can tell you for sure. However I think 99% of them will use at least some part of the TVD code as a key, otherwise they would have to introduce a specific checksum routine to stop the TVD being tampered with - this way they kill 2 birds with 1 stone!