▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ░▒▓█ rapid1337.com █▓▒░ ╓──────────────────────────────────────────╖ ║ Football Manager: World Cup Edition ║ ╟──────────────────────────────────────────╢ ║ After you have used CopylockDecoder, ║ ║ you will have a decoded file which you ║ ╟──────────────────────────────────────────╢ ║·· security/football-manager-world-cup-edi║ ╙──────────────────────────────────────────╜ ============================================================================ Title ..: Football Manager: World Cup Edition Author .: staff Date ...: 2004-10-14 Section : Security ============================================================================ ``` COPYLOCK ANALYSIS —————– After you have used CopylockDecoder, you will have a decoded file which you can disassemble. Be aware that CopylockDecoder loads the file into memory and all offsets are from the first byte of the file, not from the location of the copylock code! This is important to remember for AmigaDOS files which often have hunk header information at the top and must be offset when working out the addresses (more information to follow). Here are the main things to look at in the decoded file: lbC000448 moveq #4,d0 moveq #0,d1 lbC00044C movem.l d2-d7/a0-a3,-(sp) ;Elite crackers used to wire moveq #0,d2 ;the encrypted key in here roxr.l #3,d0 ;and branch to lbC000496 roxr.l #1,d2 roxl.l #3,d0 move.b d0,d2 move.l d1,d3 moveq #3,d7 moveq #0,d6 bsr.w lbC00055C beq.b lbC000474 moveq #1,d0 bsr.w lbC000726 bsr.w lbC000704 bsr.w lbC00049A bsr.w lbC000584 tst.l d6 bne.b lbC00048A … lbC00048A bsr.w lbC000540 move.w d2,d1 move.l d6,d0 ;Move copylock key to d0 movem.l (sp)+,d2-d7/a0-a3 lbC000496 bra.w lbC00083A ;Post copylock routine Many elite crackers back in the early 1990’s became aware that several sneaky tricks were being done in more and more copylocks. Rather than trying to emulate all the code they discovered that you could alter the encrypted copylock and wire the key in. By doing this, any sneaky tricks were still done because the game had the correct copylock key. The routine at lbC00044C would be changed to do a move.l #$b590a736,d0 and then bra lbC000496. In this tutorial we are not worried about this and will crack the game another way. The next part is how the game works out the copylock key: lbC00049A subq.w #4,sp lbC00049C move.w #$8914,d0 move.w #$32,d1 lea (lbL000802,pc),a0 bsr.w lbC0005C8 bmi.b lbC000524 move.w #7,d1 lbC0004B2 sub.l (a0)+,d0 dbra d1,lbC0004B2 cmp.l #$A573632C,d0 ;Compare magic number bne.b lbC00049C lea (lbL000802,pc),a0 lbC0004C4 move.w #$8911,d0 bsr.w lbC000602 beq.b lbC000524 cmpi.w #$2A91,(a0) bne.b lbC0004C4 move.l d0,(sp) lbC0004D6 move.w #$8912,d0 bsr.w lbC000602 beq.b lbC000524 cmpi.w #$AA92,(a0) bne.b lbC0004D6 move.l d0,d1 move.l (sp),d0 bsr.w lbC000528 bmi.b lbC000524 lbC0004F0 move.w #$8914,d0 bsr.w lbC000602 beq.b lbC000524 cmpi.w #$AA94,(a0) bne.b lbC0004F0 move.l (sp),d1 bsr.b lbC000528 bmi.b lbC000524 move.w #$8951,d0 bsr.w lbC000666 cmp.w #$3AC,d0 blt.b lbC000524 cmp.w #$53C,d0 bgt.b lbC000524 move.w #11,d1 lbC00051E sub.l (a0)+,d6 ;Key calculation routine dbra d1,lbC00051E lbC000524 addq.w #4,sp rts The critical section is lbC00051E which shows the routine that calculates the key based on the MFM data. In this case it loops around 12 times (d1 is set to 11 and it’s a dbra loop hence one extra) subtracting the MFM longword in a0 from the key d6. Now for the main decryption, relocation and memory clearing code: lbC00083A moveq #0,d1 ;Decryption code lbC00083C lea (lbC00083C,pc),a6 adda.l #$158,a6 move.l #$6400,d6 ;Decryption length add.l a6,d6 lbC00084E roxr.l #1,d0 ;Decryption loop roxr.l #1,d1 roxr.l #1,d2 roxr.l #1,d3 roxr.l #1,d4 roxr.l #1,d5 eor.l d0,(a6)+ cmpa.l d6,a6 bne.b lbC00084E lbC000860 lea (lbC000860,pc),a6 ;Relocation code move.l a6,d6 subi.l #$838,d6 adda.l #$134,a6 movem.l d0-d2/a0-a2,-(sp) cmpi.l #$3E9,(a6) ;Check hunk_code bne.b lbC0008C8 move.l (4,a6),d0 lsl.l #2,d0 lea (8,a6),a1 lea (a1,d0.l),a2 cmpi.l #$3EC,(a2)+ ;Check hunk_reloc32 bne.b lbC0008C8 lbC000894 move.l (a2)+,d1 beq.b lbC0008C8 move.l (a2)+,d0 bsr.b lbC0008B2 move.l a0,d2 tst.l (-4,a2) bne.b lbC0008A6 move.l d6,d2 lbC0008A6 move.l (a2)+,d0 add.l d2,(a1,d0.l) subq.l #1,d1 bne.b lbC0008A6 bra.b lbC000894 lbC0008B2 movea.l d6,a0 subq.l #4,a0 tst.l d0 beq.b lbC0008C4 lbC0008BA movea.l (a0),a0 adda.l a0,a0 adda.l a0,a0 subq.l #1,d0 bne.b lbC0008BA lbC0008C4 addq.l #4,a0 rts lbC0008C8 movem.l (sp)+,d0-d2/a0-a2 lea lbC0008DE(pc),a6 move.l (-4,a6),d6 add.l (8).l,d6 ori.w #$A71F,sr addi.l #$44,($24).l adda.l #$50,sp move.l #$7FFF4E73,-(sp) move.l #$584F0257,-(sp) move.l #$4CDF7FFF,-(sp) move.l #$4FEE007C,-(sp) move.l #$2D4C00BE,-(sp) move.l #$598066FA,-(sp) move.l #$67064299,-(sp) move.l #$66FA200B,-(sp) move.l #$22D85980,-(sp) move.l #$66FA6006,-(sp) move.l #$23205980,-(sp) move.l #$D1C0D3C0,-(sp) move.l #$B3C86F0C,-(sp) move.l #$200A6720,-(sp) move.l #$4E7B0002,-(sp) move.l #$202E00B8,-(sp) move.l #$4DFAFFEE,-(sp) move.l #$10,-(sp) move.l #$1423DF,-(sp) move.l #$4487A,-(sp) move.l #$BD96BDAE,-(sp) lea (*+$30,pc),a0 ;Copy from movea.l a0,a1 suba.l #$974,a1 ;Copy to movea.l #$15E9C,a2 ;Bytes to copy movea.l #$6E08,a3 ;Bytes to clear movea.l a1,a4 adda.l #0,a4 ;Return address for game move.l sp,($24).l nop nop nop nop nop Remember that I said that CopylockDecoder’s offsets are all relative to the first byte of the file in memory? In this case the file “wcfm” has $28 bytes of header information before the code starts (the copylock): dc.l $3F3 ;Hunk header dc.l 0 ;Name length (longwords) dc.l 3 ;Table size (3 hunks) dc.l 0 ;First hunk (0) dc.l 2 ;Last hunk (2) dc.l $40007329 ;Hunk size (0) dc.l $4000E267 ;Hunk size (1) dc.l $400030B8 ;Hunk size (2) dc.l $3E9 ;Hunk code dc.l $7329 ;Hunk size (longwords) lbC000028 pea (lbC000034,pc) move.l (sp)+,($10).l illegal lbC000034 movem.l d0-d7/a0-a7,-(sp) pea (lbC000054,pc) move.l (sp)+,($10).l movea.l sp,a0 movec cacr,d0 move.l d0,($3C,sp) bclr #0,d0 movec d0,cacr lbC000054 movea.l a0,sp movem.l (lbC00005C,pc),d0-d7/a0-a6 lbC00005C move.l #$4E730000,-(sp) It is critical that you remember this! All offsets are effectively $28 bytes too high relative to the file when AmigaDOS actually runs it. If you do not take this into account, the decryption will fail and the Amiga will crash! Now let’s go through this in sections, starting with the decryption routine: lbC00083A moveq #0,d1 ;Decryption code lbC00083C lea (lbC00083C,pc),a6 adda.l #$158,a6 move.l #$6400,d6 ;Decryption length add.l a6,d6 lbC00084E roxr.l #1,d0 ;Decryption loop roxr.l #1,d1 roxr.l #1,d2 roxr.l #1,d3 roxr.l #1,d4 roxr.l #1,d5 eor.l d0,(a6)+ cmpa.l d6,a6 bne.b lbC00084E This is the guts of the decrypter. After the copylock has obtained the key from the disk it loads lbC00083C into A6 and adds on $158. This is the first bit of data that will be decrypted. The loop will run and decrypt $6400 bytes of data by doing an eor.l d0,(a6)+ on each longword. In this case, all the other roxr.l’s are dummy values because registers d1 to d5 are not used. This is NOT normal however, and most games use a series of registers to decode the game. To emulate this decryption routine, we basically only need to change one line, the one at lbC00083C so a6 is set to the right value. Next up is the relocate routine. Copylock encrypts the header of the first hunk of game code and because of this, AmigaDOS was not able to relocate it. Hence the copylock routine has to do it itself: lbC000860 lea (lbC000860,pc),a6 ;Relocation code move.l a6,d6 subi.l #$838,d6 adda.l #$134,a6 movem.l d0-d2/a0-a2,-(sp) cmpi.l #$3E9,(a6) ;Check hunk_code bne.b lbC0008C8 move.l (4,a6),d0 lsl.l #2,d0 lea (8,a6),a1 lea (a1,d0.l),a2 cmpi.l #$3EC,(a2)+ ;Check hunk_reloc32 bne.b lbC0008C8 … You don’t really have to understand what it’s doing, but basically after the encrypted copylock is $6400 bytes of encrypted AmigaDOS file with header information. The copylock looks for hunk_code ($3e9) and then a hunk_reloc32 ($3ec) value. If found, it moves the file into place. To emulate this code we just need to set the correct starting address (a6) at lbC000860. Most people do not understand the last part of a copylock but it can cause games to crash if it is not done! The register a0 specifies a start address a0 (usually the code just after the copylock) and a destination a1 (which is usually the start of the file). Register a2 is used as a length, and a3 is a clear memory length: lea (*+$30,pc),a0 ;Copy from movea.l a0,a1 suba.l #$974,a1 ;Copy to movea.l #$15E9C,a2 ;Bytes to copy movea.l #$6E08,a3 ;Bytes to clear movea.l a1,a4 adda.l #0,a4 ;Return address for game move.l sp,($24).l In this case, we have to copy the decrypted game code back $974 bytes (effectively erasing the copylock – now you know why you can’t find it in memory when you run this type of copylock!). It will copy $15e9c bytes backwards and then clear the following $6e08 bytes of memory. Failure to do all this can mean the game will crash! We can rip Rob’s own code for the copy routine by pulling apart this code: adda.l #$50,sp move.l #$7FFF4E73,-(sp) move.l #$584F0257,-(sp) move.l #$4CDF7FFF,-(sp) move.l #$4FEE007C,-(sp) move.l #$2D4C00BE,-(sp) move.l #$598066FA,-(sp) move.l #$67064299,-(sp) move.l #$66FA200B,-(sp) move.l #$22D85980,-(sp) move.l #$66FA6006,-(sp) move.l #$23205980,-(sp) move.l #$D1C0D3C0,-(sp) move.l #$B3C86F0C,-(sp) move.l #$200A6720,-(sp) move.l #$4E7B0002,-(sp) move.l #$202E00B8,-(sp) move.l #$4DFAFFEE,-(sp) move.l #$10,-(sp) move.l #$1423DF,-(sp) move.l #$4487A,-(sp) move.l #$BD96BDAE,-(sp) If you run the code then have a look at the stack, you will see the following routine (which I have commmented a little bit): move.l a2,d0 ;Check anything to move beq.b .NothingToDo cmpa.l a0,a1 ble.b .CopyAscending adda.l d0,a0 adda.l d0,a1 .CopyDescending move.l -(a0),-(a1) subq.l #4,d0 bne.b .CopyDescending bra.b .CheckClearMem .CopyAscending move.l (a0)+,(a1)+ subq.l #4,d0 bne.b .CopyAscending .CheckClearMem move.l a3,d0 beq.b .NothingToDo .ClearLoop clr.l (a1)+ subq.l #4,d0 bne.b .ClearLoop And that is the analysis section complete! Now we have to write a new loader for the game. WRITING THE LOADER —————— The way I will crack this game is the cleanest method possible – by writing a new program which will load the copy protected file, emulate the protection and then start it up. Most of the code will be lifted directly from the copylock routine! To start off we must open dos.library and LoadSeg the main file: lea (_dosname,pc),a1 ;Open dos.library move.l (4),a6 jsr (_LVOOldOpenLibrary,a6) lea (_dosbase,pc),a0 move.l d0,(a0) move.l d0,a6 ;A6 = dosbase lea _program(pc),a0 ;Load exe move.l a0,d1 jsr (_LVOLoadSeg,a6) move.l d0,d7 ;D7 = segment beq _failedtoload bsr _DecryptRob ;Decrypt and relocate The DecryptRob routine will use the d7 register to work out where it needs to patch the code. LoadSeg returns a BPTR which is a longword number pointer to the first segment (not the code). To convert from a BPTR to a real Amiga address, you multiply by 4 and then add 4 if you want the code address. Therefore: add.l d7,d7 add.l d7,d7 add.l #4,d7 move.l d7,a3 A3 is now pointing to the first actual piece of code. We can check now that we have found the correct code or abort: cmp.l #$104afc,$8(a3) bne _wrongver If the value $104afc wasn’t found 8 bytes away, we have made a mistake or the loader is loading the wrong file. Now just copy all the copylock code into and assemble it. The code below stores all registers before starting the game which is vital for some games which are compiled with MANX C. It then cracks the game, restores registers, invokes the game as if started from the CLI with no parameters (a0/d0), and will cause the screen to turn a certain colour if anything goes wrong and hang rather than elegantly quitting, but it is purely shown as an example. The full crack code is: ;*————————————————————————— ; Program: FootballManagerWCE.s ; Contents: Crack for “Football Manager: World Cup Edition” (c) 1990 ; Addictive ; Author: Codetapper of Action ; History: 10.10.04 – v1.0 ; – Rob Northen Copylock removed ; Requires: An Amiga 🙂 ; Copyright: Public Domain ; Language: 68000 Assembler ; Translator: Barfly ;—————————————————————————* INCDIR Include: INCLUDE lvo/dos.i INCLUDE lvo/exec.i IFD BARFLY OUTPUT “FootballManagerWCE” BOPT O+ ;enable optimizing BOPT OG+ ;enable optimizing BOPT ODd- ;disable mul optimizing BOPT ODe- ;disable mul optimizing BOPT w4- ;disable 64k warnings BOPT wo- ;disable warnings SUPER ;disable supervisor warnings ENDC ;====================================================================== bra _FootballManWCE dc.b “Cracked by Codetapper/Action on 11/10/2004!”,0 EVEN _FootballManWCE lea _saveregs(pc),a0 movem.l d1-d6/a2-a6,(a0) move.l (a7)+,(44,a0) lea (_dosname,pc),a1 ;Open dos.library move.l (4),a6 jsr (_LVOOldOpenLibrary,a6) lea (_dosbase,pc),a0 move.l d0,(a0) move.l d0,a6 ;A6 = dosbase lea _program(pc),a0 ;Load exe move.l a0,d1 jsr (_LVOLoadSeg,a6) move.l d0,d7 ;D7 = segment beq _failedtoload ;bra .end bsr _DecryptRob ;Decrypt and relocate move.l d7,a1 ;Start game add.l a1,a1 add.l a1,a1 moveq #_args_end-_args,d0 lea (_args,pc),a0 movem.l (_saveregs,pc),d1-d6/a2-a6 jsr (4,a1) bra _ok _saveregs ds.l 11 _saverts dc.l 0 _dosbase dc.l 0 _program dc.b “wcfm”,0 _args dc.b 10 _args_end dc.b 0 _dosname dc.b “dos.library”,0 EVEN ;====================================================================== _DecryptRob movem.l d0-d7/a0-a6,-(sp) add.l d7,d7 add.l d7,d7 add.l #4,d7 move.l d7,a3 ;a3 = Spare register cmp.l #$104afc,$8(a3) bne _wrongver move.l #$b590a736,d0 moveq #0,d1 move.l #$0c007ebc,d2 move.l #$00000c87,d3 move.l #$ffff0cd5,d4 move.l #$00000002,d5 lea $83c-$28(a3),a6 adda.l #$158,a6 move.l #$6400,d6 add.l a6,d6 .Decrypt roxr.l #1,d0 roxr.l #1,d1 roxr.l #1,d2 roxr.l #1,d3 roxr.l #1,d4 roxr.l #1,d5 eor.l d0,(a6)+ cmpa.l d6,a6 bne.b .Decrypt lea $860-$28(a3),a6 ;Process hunks move.l a6,d6 subi.l #$838,d6 adda.l #$134,a6 movem.l d0-d2/a0-a2,-(sp) cmpi.l #$3E9,(a6) ;Check hunk_code bne.b .HunkProcDone move.l (4,a6),d0 lsl.l #2,d0 lea (8,a6),a1 lea (a1,d0.l),a2 cmpi.l #$3EC,(a2)+ ;Check hunk_reloc32 bne.b .HunkProcDone .NextHunk move.l (a2)+,d1 beq.b .HunkProcDone move.l (a2)+,d0 bsr.b .ProcessHunk move.l a0,d2 tst.l (-4,a2) bne.b .AddRelocValue move.l d6,d2 .AddRelocValue move.l (a2)+,d0 add.l d2,(a1,d0.l) subq.l #1,d1 bne.b .AddRelocValue bra.b .NextHunk .ProcessHunk movea.l d6,a0 subq.l #4,a0 tst.l d0 beq.b .HunkLoopDone .SkipHunks movea.l (a0),a0 adda.l a0,a0 adda.l a0,a0 subq.l #1,d0 bne.b .SkipHunks .HunkLoopDone addq.l #4,a0 rts .HunkProcDone movem.l (sp)+,d0-d2/a0-a2 lea $99c-$28(a3),a0 ;Setup relocation parameters movea.l a0,a1 ;(ripped from the copylock) suba.l #$974,a1 movea.l #$15e9c,a2 movea.l #$6e08,a3 movea.l a1,a4 move.l a2,d0 ;Code from copylock beq.b .NothingToDo cmpa.l a0,a1 ble.b .CopyAscending adda.l d0,a0 adda.l d0,a1 .CopyDescending move.l -(a0),-(a1) subq.l #4,d0 bne.b .CopyDescending bra.b .CheckClearMem .CopyAscending move.l (a0)+,(a1)+ subq.l #4,d0 bne.b .CopyAscending .CheckClearMem move.l a3,d0 beq.b .NothingToDo .ClearLoop clr.l (a1)+ subq.l #4,d0 bne.b .ClearLoop .NothingToDo movem.l (sp)+,d0-d7/a0-a6 rts ;====================================================================== _wrongver move.w #$fff,d0 bra _end _failedtoload move.w #$f00,d0 bra _end _ok move.w #$0f0,d0 _end move.w d0,$dff180 nop nop nop nop bra _end Now all you need to do is copy the new loader onto the disk and alter the startup-sequence to run your cracktro, then the file “FootballManagerWCE” instead of “wcmf”! ``` ---------------------------------------------------------------------------- -- rapid1337.com -- The dumping ground for all things tech // knowledge is the weapon --