Index du Forum
IDENTIFICATION SERVEUR : 10.0.97.1 - CLIENT : 54.235.48.106

 FAQFAQ   RechercherRechercher   Liste des MembresListe des Membres   Groupes d'utilisateursGroupes d'utilisateurs   S'enregistrerS'enregistrer 
 ProfilProfil   Se connecter pour vérifier ses messages privésSe connecter pour vérifier ses messages privés   ConnexionConnexion 

Hard Hat Mack (Electronic Arts, 1983)

 
Poster un nouveau sujet   Répondre au sujet     Index du Forum -> PROTECTION MALEFIQUE
Voir le sujet précédent :: Voir le sujet suivant  
Auteur Message
qkumba



Inscrit le: 29 Jan 2012
Messages: 176

MessagePosté le: Mar 08 Sep 2015, 6:16    Sujet du message: Hard Hat Mack (Electronic Arts, 1983) Répondre en citant

This is a Donkey Kong-style game. Help Mack to fix three different buildings, while avoiding the enemies, the flying bolts and other obstacles, and even the edge of the building itself!

We start with the boot sector:
Code:
8600<C600.C6FCM
86F9:59 FF
8600G

0800    .BYTE $10 ;yes, the entire track
;start here
0801    JMP    $80E
...
080E    BIT    $FF
;copy self (4kb) to $B000-BFFF
0810    LDX    #0
0812    LDA    $800,X
0815    STA    $B000,X
0818    INX
0819    BNE    $812
081B    INC    $814
081E    INC    $817
0821    LDA    $817
0824    CMP    #$C0
0826    BNE    $812
;and continue from there
0828    JMP    $B047

86F8:A9 5 8D 29 8 A9 87 8D 2A 8 4C 1 8 4C 59 FF
8600G

;erase both graphics screens and $6000-7FFF as collateral damage to reduce code size
B047    LDA    #$FF
B049    LDX    #0
B04B    STA    $2000,X
B04E    INX
B04F    BNE    $B04B
B051    INC    $B04D
B054    BPL    $B04B
;copy first section of logo to screen
B056    LDX    #0
B058    JSR    $B080
B05B    INX
B05C    CPX    #$20
B05E    BNE    $B058
;copy second section of logo to screen
B060    LDX    #$80
B062    JSR    $B080
B065    INX
B066    CPX    #$A0
B068    BNE    $B062
;for the whole first graphics screen
B06A    INC    $B085
B06D    LDA    $B085
B070    CMP    #$40
B072    BNE    $B056
;display logo
B074    LDA    $C057
B077    LDA    $C052
B07A    LDA    $C050
;and jump again
B07D    JMP    $B900
;just a byte copier
B080    LDA    $B100
B083    STA    $202C,X
B086    INC    $B081
B089    BNE    $B08E
B08B    INC    $B082
B08E    RTS


Nothing special, until...

Code:
;and at $B900...
B900    LDX    #$30
B902    JSR    $BA31
B905    DEX
B906    BPL    $B900
;yeah, that doesn't help, unless you've seen it before.
;this is the entrypoint to Electronic Arts' p-code protection engine
;used by Last Gladiator, Archon, Marble Madness, and some other titles including, of course, Hard Hat Mack
;that loop is really asking for a breakpoint to be placed just after it, right?  ha, don't do that.

;p-code init
BA31    LDA    $3E
BA33    PHA
BA34    TYA
BA35    PHA
BA36    JSR    $BA8F
...
;pop return to $BA38, and save it in $42-43
BA8F    PLA
BA90    STA    $42
BA92    PLA
BA93    STA    $43
;discard the saved Y and $3E values
BA95    PLA
BA96    STA    $D8
BA98    PLA
BA99    STA    $D8
;adjust address to account for 6502 prefetch behaviour
BA9B    INC    $42
BA9D    BNE    $BAA1
BA9F    INC    $43
;and resume from $BA39
BAA1    JMP    ($42)

;pop return to $B904, and save it in $D8
BA39    PLA
BA3A    STA    $D8
BA3C    PLA
BA3D    STA    $D9
;fetch from $B904+4=$B908
;where we wanted to put our breakpoint
BA3F    LDY    #4
BA41    LDA    ($D8),Y
;point to next byte of p-code
BA43    INY
BA44    BNE    $BA48
BA46    INC    $D9
;used fetch value as index into 8-bit dispatch table
BA48    TAX
BA49    LDA    $BA5C,X
;all values are relative to $BA68
BA4C    CLC
BA4D    ADC    #$68
BA4F    STA    $BA5A
BA52    LDA    #$BA
BA54    ADC    #0
BA56    STA    $BA5B
;self-modified dispatcher
BA59    JMP    $BA31
BA5C    .BYTE 0, $3C, $53, $5D, $6B, $88, $AB, $BC, $4D, $A0, $77, $B7
Revenir en haut de page
Voir le profil de l'utilisateur Envoyer un message privé Visiter le site web de l'utilisateur
qkumba



Inscrit le: 29 Jan 2012
Messages: 176

MessagePosté le: Mar 08 Sep 2015, 6:19    Sujet du message: Répondre en citant

Now we are right into the p-code. This version has only 12 instructions.
All parameters to the instructions are xor-encoded, just to complicate things a bit.
The instructions are:

Code:
00      JMP
01      CALL NATIVE
02      BEQ
03      LDA IMM
04      LDA ABSOLUTE
05      JSR
06      STA ABSOLUTE
07      SBC IMM
08      JMP NATIVE
09      RTS
0A      LDA ABSOLUTE, A ;p-code A register
0B      ASL


and they look like this:

Code:
00
;fetch 16-bit value
BA68    JSR    $BA78
;copy 16-bit value to p-code pointer
BA6B    LDA    $42
BA6D    STA    $D8
BA6F    LDA    $43
BA71    STA    $D9
BA73    LDY    #0
;resume execution
BA75    JMP    $BA41


Code:
01
;fetch 16-bit value
BAA4    JSR    $BA78
;save instruction pointer on stack
BAA7    TYA
BAA8    PHA
;load p-code A register
BAA9    LDA    $DC
;run native code
BAAB    JSR    $BAB8
;save p-code A register
BAAE    STA    $DC
;restore instruction pointer from stack
BAB0    PLA
BAB1    TAY
;resume execution
BAB2    JMP    $BA41
...
BAB8    JMP    ($42)


Code:
02
;fetch 16-bit value
BABB    JSR    $BA78
;load p-code A register
BABE    LDA    $DC
;branch to update instruction pointer if true
BAC0    BEQ    $BA6B
;fall through if false, and resume execution
BAC2    JMP    $BA41
...
;copy 16-bit value to p-code pointer
BA6B    LDA    $42
BA6D    STA    $D8
BA6F    LDA    $43
BA71    STA    $D9
;resume execution
BA73    LDY    #0
BA75    JMP    $BA41


Code:
03
;fetch 8-bit value
BAC5    LDA    ($D8),Y
BAC7    INY
BAC8    BNE    $BACC
BACA    INC    $D9
;decode and store it in p-code A register
BACC    EOR    #$4C
BACE    STA    $DC
;resume execution
BAD0    JMP    $BA41


Code:
04
;fetch 16-bit value
BAD3    JSR     $BA78
;load 8-bit value indirectly
BAD6    LDX     #0
BAD8    LDA     ($42,X)
;store it in p-code A register
BADA    STA     $DC
;resume execution
BADC    JMP     $BA41


Code:
05
;fetch 16-bit value
BAF0    JSR    $BA78
;adjust for current instruction pointer
BAF3    TYA
BAF4    CLC
BAF5    ADC    $D8
BAF7    STA    $D8
BAF9    BCC    $BAFD
BAFB    INC    $D9
;save current instruction pointer
BAFD    LDA    $D8
BAFF    PHA
BB00    LDA    $D9
BB02    PHA
;update instruction pointer
BB03    LDY    #0
BB05    JMP    $BA6B
...
;copy 16-bit value to p-code pointer
BA6B    LDA    $42
BA6D    STA    $D8
BA6F    LDA    $43
BA71    STA    $D9
;resume execution
BA73    LDY    #0
BA75    JMP    $BA41


Code:
06
;fetch 16-bit value
BB13    JSR    $BA78
;load p-code A register
BB16    LDA    $DC
;store 8-bit value indirectly
BB18    LDX    #0
BB1A    STA    ($42,X)
;resume execution
BB1C    JMP    $BA41


Code:
07
;fetch 8-bit value
BB24    LDA    ($D8),Y
;adjust instruction pointer
BB26    INY
BB27    BNE    $BB2B
BB29    INC    $D9
;decode and store it in temp
BB2B    EOR    #$4C
BB2D    STA    $42
;subtract from p-code A register
BB2F    LDA    $DC
BB31    SEC
BB32    SBC    $42
;update p-code A register
BB34    STA    $DC
;resume execution
BB36    JMP    $BA41


Code:
08
;fetch 16-bit value
BAB5    JSR    $BA78
;jump there
BAB8    JMP    ($42)


Code:
09
;pop return address into p-code instruction pointer
BB08    PLA
BB09    STA    $D9
BB0B    PLA
BB0C    STA    $D8
;resume execution
BB0E    LDY    #0
BB10    JMP    $BA41


Code:
0A
;fetch 16-bit value
BADF    JSR    $BA78
;add it to p-code A register
BAE2    LDA    $DC
BAE4    CLC
BAE5    ADC    $42
BAE7    STA    $42
BAE9    BCC    $BAED
BAEB    INC    $43
BAED    JMP    $BAD6
...
;fetch 8-bit value
BAD6    LDX    #0
BAD8    LDA    ($42,X)
;store it in p-code A register
BADA    STA    $DC
;resume execution
BADC    JMP    $BA41


Code:
0B
;left-shift p-code A register
BB1F    ASL    $DC
;resume execution
BB21    JMP    $BA41


Code:
fetch16 helper function:
;fetch 16-bit value to temp
BA78    LDA    ($D8),Y
BA7A    EOR    #3
BA7C    INY
BA7D    BNE    $BA81
BA7F    INC    $D9
BA81    STA    $42
BA83    LDA    ($D8),Y
BA85    INY
BA86    BNE    $BA8A
BA88    INC    $D9
BA8A    EOR    #$D9
BA8C    STA    $43
BA8E    RTS
Revenir en haut de page
Voir le profil de l'utilisateur Envoyer un message privé Visiter le site web de l'utilisateur
qkumba



Inscrit le: 29 Jan 2012
Messages: 176

MessagePosté le: Mar 08 Sep 2015, 6:22    Sujet du message: Répondre en citant

Now for the p-code itself:

Code:
B908    .BYTE  3, $4C            ;LDA #0
B90A    .BYTE  6, $C4, $65       ;STA $BCC7
B90D    .BYTE  4,   5, $D1       ;LDA $806      ;probably a developer hook
B910    .BYTE  2, $22, $60       ;BEQ $B921     ;always taken
B913    .BYTE  3, $4C            ;LDA #$00
B915    .BYTE  5, $92, $60       ;JSR $B991     ;read sectors
B918    .BYTE  4, $EB, $19       ;LDA $C0E8
B91B    .BYTE  1, $42, $69       ;CALL $B041    ;nothing, see below
B91E    .BYTE  1, $7C, $60       ;CALL $B97F    ;call stack swap routine
B921    .BYTE  4, $82, $19       ;LDA $C081
B924    .BYTE  4, $82, $19       ;LDA $C081
B927    .BYTE  3, $49            ;LDA #5
B929    .BYTE  5, $92, $60       ;JSR $B991     ;read sectors
B92C    .BYTE  4, $B,  $D1       ;LDA $B908
B92F    .BYTE  2, $34, $60       ;BEQ $B937
B932    .BYTE  3, $42            ;LDA #$0E
B934    .BYTE  5, $92, $60       ;JSR $B991     ;read sectors
B937    .BYTE  1, $3E, $60       ;CALL $B93D    ;clear first text screen
B93A    .BYTE  0, $54, $60       ;JMP $B957
...
B991    .BYTE  6, $DF, $60       ;STA $B9DC     ;set initial index
B994    .BYTE  3, $4C            ;LDA #0
B996    .BYTE  6, $C4, $65       ;STA $BCC7     ;clear error code
B999    .BYTE  4, $EA, $19       ;LDA $C0E9     ;turn on drive
B99C    .BYTE  4, $DF, $60       ;LDA $B9DC
B99F    .BYTE $A, $DE, $60       ;LDA $B9DD, A  ;index into address table
B9A2    .BYTE  2, $BF, $60       ;BEQ $B9BC     ;exit when no address
B9A5    .BYTE  6, $3D, $D9       ;STA $3E       ;save address
B9A8    .BYTE  4, $DF, $60       ;LDA $B9DC
B9AB    .BYTE $A, $F4, $60       ;LDA $B9F7, A  ;index into track table
B9AE    .BYTE  1,   3, $66       ;CALL $BF00    ;seek/read
B9B1    .BYTE  4, $DF, $60       ;LDA $B9DC
B9B4    .BYTE  7, $B3            ;SBC #$FF      ;+1 because p-code has no INC
B9B6    .BYTE  6, $DF, $60       ;STA $B9DC     ;increment index
B9B9    .BYTE  0, $9F, $60       ;JMP $B99C     ;loop
B9BC    .BYTE  4, $C4, $65       ;LDA $BCC7     ;check error code
B9BF    .BYTE  2, $D8, $60       ;BEQ $B9DB     ;return if okay
B9C2    .BYTE  1, $5B, $25       ;CALL $FC58    ;clear screen
B9C5    .BYTE  1, $DE, $22       ;CALL $FBDD    ;bell
B9C8    .BYTE  4, $52, $19       ;LDA $C051     ;text mode
B9CB    .BYTE  3, $89            ;LDA #$C5
B9CD    .BYTE  6,   3, $DD       ;STA $400      ;E
B9D0    .BYTE  3, $9E            ;LDA #$D2
B9D2    .BYTE  6,   2, $DD       ;STA $401      ;R
B9D5    .BYTE  6,   1, $DD       ;STA $402      ;R
;note that this is a p-code instruction,
so interpreter is still running
;it's just not going anywhere
;running on the spot, if you will
B9D8    .BYTE  0, $DB, $60       ;JMP $B9D8
B9DB    .BYTE  9                 ;RTS


Code:
B041    JMP    ($B00A)                      ;probably debugging interface, undefined now


Code:
;stack swap routine (was probably also called by the $B00A handler)
;further evidence of developer interface
B97F    LDX    #$1F
B981    LDA    $BA11,X
B984    PHA
B985    LDA    $E0,X
B987    STA    $BA11,X
B98A    PLA
B98B    STA    $E0,X
B98D    DEX
B98E    BPL    $B981
B990    RTS


Code:
;clear first text screen
B93D    LDA    #4
B93F    STA    $B948
B942    LDX    #0
B944    LDA    #$A0
B946    STA    $400,X
B949    INX
B94A    BNE    $B944
B94C    INC    $B948
B94F    LDA    $B948
B952    CMP    #8
B954    BNE    $B944
B956    RTS


Code:
B957    .BYTE  4,   4, $D1     ;LDA $807       ;more flags
B95A    .BYTE  2, $63, $60     ;BEQ $B960      ;always taken
B95D    .BYTE  4, $52, $19     ;LDA $C051
B960    .BYTE  3, $5E          ;LDA #$12
B962    .BYTE  5, $92, $60     ;JSR $B991      ;read sectors
B965    .BYTE  1, $CB, $62     ;CALL $BBC8
B968    .BYTE  3, $6D          ;LDA #$21
B96A    .BYTE  1, $CB, $65     ;CALL $BCC8     ;seek
B96D    .BYTE  8,   3, $4F     ;JMP $9600      ;mystery code


Code:
BBC8    JSR    $BB39                           ;check copy-protection
BBCB    JSR    $BA31                           ;another p-code layer on failure
BBCE    DEX
BBCF    BPL    $BBCB
BBD1    .BYTE  0, $C1, $60     ;JMP $B9C2      ;the ERR routine above


Code:
BF00    JSR    $BCC8    ;standard seek routine
BF03    LDY    #$F
BF05    LDA    $BF48,Y  ;fetch sector from translate table
BF08    CLC
BF09    ADC    $3E      ;adjust for address
BF0B    STA    $BF58,Y  ;store in buffer
BF0E    DEY
BF0F    BPL    $BF05    ;16 sectors
BF11    LDX    $2B      ;slot
BF13    LDA    #$60
BF15    STA    $40      ;retry count
BF17    DEC    $40
BF19    BEQ    $BF44    ;branch if failure
BF1B    JSR    $BD15
BF1E    BCS    $BF17    ;read address field, standard prologue and epilogue
BF20    LDY    $2D
BF22    LDA    $BF58,Y  ;look up address in buffer
BF25    BEQ    $BF17    ;skip if done already
BF27    STA    $45      ;remember it
BF29    LDA    #0
BF2B    STA    $44      ;zero low byte of address
BF2D    JSR    $BC02    ;read data field, #$D5 #$BB #$CF, and checks only one epilogue byte
                        ;routine is interleaved read used by ProDOS, but dates back to 1980
BF30    BCS    $BF17    ;branch if error
BF32    LDY    $2D
BF34    LDA    #0
BF36    STA    $BF58,Y  ;zero entry in buffer
BF39    LDY    #$F
BF3B    LDA    $BF58,Y  ;scan table
BF3E    BNE    $BF17    ;branch if any left
BF40    DEY
BF41    BPL    $BF3B    ;search all entries
BF43    RTS
BF44    INC    $BCC7    ;set error code
BF47    RTS


And now the copy-protection routine.

Code:
BB39    LDA    $C0E9    ;turn on the drive
BB3C    LDX    #$FF
BB3E    STX    $4       ;set minimum time
BB40    INX
BB41    STX    $1       ;clear error code (unused)
BB43    STX    $3       ;set maximum time
BB45    LDA    #$18
BB47    STA    $6       ;set track
BB49    JSR    $BCC8    ;initial seek
BB4C    INC    $6       ;advance by half track
BB4E    JSR    $BD15    ;read address prologue
BB51    BCS    $BB4E    ;branch if error
BB53    LDA    $2D
BB55    BNE    $BB4E    ;watch for sector 0
BB57    LDA    $6
BB59    JSR    $BCC8    ;seek
BB5C    INC    $6       ;advance by half track
BB5E    LDA    #$19
BB60    JSR    $BD08    ;delay
BB63    LDA    #0
BB65    STA    $0       ;set retry counter
BB67    INC    $0
BB69    BEQ    $BB9E    ;quit if too many
BB6B    LDA    $C0EC
BB6E    BPL    $BB6B    ;wait for nibble to arrive
BB70    CMP    #$D5
BB72    BNE    $BB67    ;watch for #$D5
BB74    LDA    $C0EC
BB77    BPL    $BB74    ;wait for nibble to arrive
BB79    CMP    #$AA
BB7B    BNE    $BB70    ;watch for #$AA
BB7D    LDY    #2
BB7F    LDA    $C0EC
BB82    BPL    $BB7F    ;wait for nibble to arrive
BB84    CMP    #$96
BB86    BNE    $BB70    ;watch for #$96
BB88    LDA    $C0EC
BB8B    BPL    $BB88    ;wait for nibble to arrive
BB8D    ROL             ;decode
BB8E    STA    $2       ;store half
BB90    LDA    $C0EC
BB93    BPL    $BB90    ;wait for nibble to arrive
BB95    AND    $2       ;combine
BB97    DEY
BB98    BPL    $BB88    ;discard volume and track, keep sector
BB9A    CMP    #2
BB9C    BEQ    $BB9F    ;must be sector 2
BB9E    RTS             ;else fail
BB9F    LDA    $0
BBA1    CMP    $3       ;check seek time
BBA3    BCC    $BBA7    ;skip if faster
BBA5    STA    $3       ;save slowest time
BBA7    CMP    $4
BBA9    BCS    $BBAD    ;skip if slower
BBAB    STA    $4       ;save fastest time
BBAD    LDA    $6
BBAF    CMP    #$1F     ;repeat until track 15.5
BBB1    BEQ    $BBB6
BBB3    JMP    $BB4E
BBB6    LDA    $3
BBB8    SEC
BBB9    SBC    $4       ;calculate delta time
BBBB    CMP    #$22
BBBD    BCC    $BBC0    ;accept if not too long
BBBF    RTS
BBC0    LDA    $1
BBC2    BNE    $BBC7    ;fail if error (unreachable)
BBC4    JMP    $BBD4
BBC7    RTS
...
BBD4    PLA             ;success
BBD5    PLA
BBD6    RTS


So this routine requires synchronised sector position across multiple tracks.
How do we proceed? First, we change the data prologue back to #$D5 #$AA #$AD.
T00 S09 A39, BB -> AA
T00 S09 A43, CF -> AD

Then we exploit our p-code prowess to bypass the routine entirely.
The copy-protection is reached by a call to $BBC8. That call comes from $B96A.
Immediately before $BBC8 is a RTS. Let's patch the p-code to call that instead.

T00 S03 A66, CB -> C4


Dernière édition par qkumba le Mer 09 Sep 2015, 6:20; édité 1 fois
Revenir en haut de page
Voir le profil de l'utilisateur Envoyer un message privé Visiter le site web de l'utilisateur
qkumba



Inscrit le: 29 Jan 2012
Messages: 176

MessagePosté le: Mer 09 Sep 2015, 6:15    Sujet du message: Répondre en citant

...and then AppleWin bites me in the arse. It works there, but we're not done after all, and the disk is still protected. I'm a MAME developer, I should have been using MAME (which shows the protection failure). I have no excuse.
Further disassembly shows another p-code engine at $AD00.
This one has only 11 instructions. Fortunately, 10 of them are identical to the previous engine, only ASL is missing, and the "LDA ABSOLUTE, A" is now "INC ABSOLUTE".

Code:
AD00    JMP    $AD62
...
AD62    JSR    $AE64
AD65    JMP    $AD62


Code:
AD68    .BYTE  5, $83, $74      ;JSR $AD80      ;check synchronised sectors
AD6B    .BYTE  2, $7E, $74      ;BEQ $AD7D      ;skip if success
AD6E    .BYTE  5, $83, $74      ;JSR $AD80      ;check synchronised sectors
AD71    .BYTE  2, $7E, $74      ;BEQ $AD7D      ;skip if success
AD74    .BYTE  5, $83, $74      ;JSR $AD80      ;check synchronised sectors
AD77    .BYTE  2, $7E, $74      ;BEQ $AD7D      ;skip if success
AD7A    .BYTE  0, $47, $77      ;JMP $AE44      ;quit to monitor
AD7D    .BYTE  8, $AE, $74      ;JMP $ADAD      ;pass
AD80    .BYTE  4, $EA, $19      ;LDA $C0E9
AD83    .BYTE  4, $ED, $19      ;LDA $C0EE
AD86    .BYTE  3, $4C           ;LDA #$00
AD88    .BYTE  6, $91, $77      ;STA $AE92      ;clear error code
AD8B    .BYTE  4, $E4, $19      ;LDA $C0E7      ;step
AD8E    .BYTE  5, $E3, $74      ;JSR $ADE0      ;check sector
AD91    .BYTE  4, $E6, $19      ;LDA $C0E5      ;step
AD94    .BYTE  5, $E3, $74      ;JSR $ADE0      ;check sector
AD97    .BYTE  4, $EB, $19      ;LDA $C0E8
AD9A    .BYTE  4, $91, $77      ;LDA $AE92      ;load error code
AD9D    .BYTE  9                ;RTS
...
ADAD    RTS
...
AE44    .BYTE  8, $5A, $26      ;JMP $FF59
...
ADE0    .BYTE  3, $E6           ;LDA #$AA
ADE2    .BYTE  1, $3A, $77      ;CALL $AE39     ;delay
ADE5    .BYTE  4, $E3, $19      ;LDA $C0E0
ADE8    .BYTE  4, $E1, $19      ;LDA $C0E2
ADEB    .BYTE  4, $E7, $19      ;LDA $C0E4
ADEE    .BYTE  4, $E5, $19      ;LDA $C0E6
ADF1    .BYTE  3, $BC           ;LDA #$F0
ADF3    .BYTE  6, $92, $77      ;STA $AE91      ;set initial sector to expect
ADF6    .BYTE  3, $7C           ;LDA #$30
ADF8    .BYTE  6, $90, $77      ;STA $AE93      ;set retry count
ADFB    .BYTE  5, $FC, $74      ;JSR $ADFF
ADFE    .BYTE  9                ;RTS
ADFF    .BYTE  1, $17, $77      ;CALL $AE14     ;verify first sector that arrives
AE02    .BYTE  3, $59           ;LDA #$15
AE04    .BYTE  6, $90, $77      ;STA $AE93
AE07    .BYTE $A, $92, $77      ;INC $AE91
AE0A    .BYTE  2, $13, $77      ;BEQ $AE10
AE0D    .BYTE  0, $FC, $74      ;JMP $ADFF
AE10    .BYTE  9                ;RTS
...
AE14    JSR    $AD9E            ;read address prologue
AE17    BCC    $AE1C
AE19    JSR    $AD9E            ;read address prologue
AE1C    BCC    $AE21
AE1E    JSR    $AD9E            ;read address prologue
AE21    BCS    $AE30            ;give up after 3 tries
AE23    ORA    #$F0
AE25    CMP    $AE91            ;compare with requested sector
AE28    BNE    $AE30
AE2A    JSR    $AD03            ;measure sector size
AE2D    BCS    $AE30
AE2F    RTS
AE30    DEC    $AE93
AE33    BPL    $AE14            ;retry while count left
AE35    INC    $AE92            ;set error code
AE38    RTS
;delay
AE39    LDX    #$20
AE3B    DEX
AE3C    BNE    $AE3B
AE3E    SEC
AE3F    SBC    #1
AE41    BNE    $AE39
AE43    RTS


This protection checks that all 16 sectors in a track are in incremental order.
How did it even get there? In between is yet another p-code engine. This one is a stack-based engine, entirely different from the one that we've been analysing so far.
Yes, that's the mysterious code at $9600.

Code:
9600    NOP
9601    JMP    $9628
...
9628    LDA    #$6C
962A    STA    $DB
962C    LDA    $9610
962F    STA    $DE
9631    LDA    $9611
9634    STA    $DF
9636    LDA    xstkpointer
9639    TAX
963A    TXS
963B    LDX    $9612
963E    LDA    #$96
9640    STA    $DA
9642    LDA    #$26
9644    STA    $D9
9646    JMP    $9672
...
9672    LDY    #1
9674    LDA    ($D9),Y
9676    STA    $DD
9678    DEY
9679    LDA    ($D9),Y
967B    STA    $DC
967D    CLC
967E    LDA    $D9
9680    ADC    #2
9682    STA    $D9
9684    BCC    $9688
9686    INC    $DA
9688    JMP    $DB


And now its p-code:

Code:
9626    .WORD xbegin
...
xbegin
9C5C    .WORD xsave_retpc
9C5E    .WORD xpush_val0
9C60    .WORD xpush_var9AF7
9C62    .WORD xset_var_poppop   ; set var0 to 0
9C64    .WORD xtext_home
9C66    .WORD xpush_imm
9C68    .WORD $12
9C6A    .WORD xget_caddr        ; pea constants+#$12
9C6C    .WORD xpush_imm
9C6E    .WORD $DE
9C70    .WORD xread_stk_ind     ; fetch scratch address
9C72    .WORD xpush_imm
9C74    .WORD 6
9C76    .WORD xadd_stk_pop      ; pea scratch+6
9C78    .WORD xpush_imm
9C7A    .WORD $10
9C7C    .WORD xmemcpy           ; copy #$10 bytes from constants+#$12 to scratch+6
9C7E    .WORD xpush_imm
9C80    .WORD $C
9C82    .WORD xget_caddr
9C84    .WORD xread_stk_ind     ; read from constants+#$0c
9C86    .WORD xpush_imm
9C88    .WORD $FD1B
9C8A    .WORD xpush_imm
9C8C    .WORD $38
9C8E    .WORD xset_var_poppop   ; set $38-39 to $FD1B
9C90    .WORD xpush_imm
9C92    .WORD $FDF0
9C94    .WORD xpush_imm
9C96    .WORD $36
9C98    .WORD xset_var_poppop   ; set $36-37 to $FDF0
9C9A    .WORD $9C52
9C9C    .WORD xjmp_retpc
...
9C52    .WORD xsave_retpc
9C54    .WORD xset_pstk
9C56    .WORD xset_nstk
9C58    .WORD $9F23
9C5A    .WORD xjmp_retpc
...
9F23    .WORD xsave_retpc
9F25    .WORD $9E87
9F27    .WORD xpush_imm
9F29    .WORD $FF
9F2B    .WORD xpush_imm
9F2D    .WORD $32
9F2F    .WORD xset_mem_pop_pop
9F31    .WORD $9F1E
9F33    .WORD xjmp_retpc
...
9E87    .WORD xsave_retpc
9E89    .WORD xset_var9CC2_5555
9E8B    .WORD xpush_val800
9E8D    .WORD xpush_var9CB6
9E8F    .WORD xread_stk_ind
9E91    .WORD xpush_val1
9E93    .WORD xset_var_div
9E95    .WORD xset_var9CB2_800
9E97    .WORD xset_var9CC2_5555
9E99    .WORD decide_chk_prot


It's not using byte-code like the other one. It's just function pointers and parameters.
It goes on and on, moving bits around in memory, and decoding the game itself.
Revenir en haut de page
Voir le profil de l'utilisateur Envoyer un message privé Visiter le site web de l'utilisateur
qkumba



Inscrit le: 29 Jan 2012
Messages: 176

MessagePosté le: Mer 09 Sep 2015, 6:18    Sujet du message: Répondre en citant

The first thing that we care about is that last line above, because that's where the decision is made to check the copy protection.

Code:
decide_chk_prot
9DF2    .WORD xsave_retpc
9DF4    .WORD xpush_imm
9DF6    .WORD $95FF
9DF8    .WORD xpush_imm
9DFA    .WORD $A600
9DFC    .WORD xchkstk_vars
9DFE    .WORD xbeq_rel
9E00    .WORD 4
9E02    .WORD xdo_copy_prot
9E04    .WORD xjmp_retpc


The routine is checking if the copy protection code is present, by checking the start and end addresses for the code.
If they were the same, then the protection would not be called.

Code:
xdo_copy_prot
9DE4    .WORD $9DE6
9DE6    STX     $9DE2
9DE9    JSR     $AD00
9DEC    LDX     $9DE2
9DEF    JMP     $9672


There is our call to $AD00, but let's go back to the p-code.

Code:
9DFE    .WORD xbeq_rel
9E00    .WORD 4


How about we show off more and just change that to xjmp_rel instead?
T0D S08 AFE, C0 -> AB

How about now? Does the game work? The title screen comes up, the demo runs, and then you press a key to start... no, still not. It reboots instead.
What's next? The second thing that we care about is how the game ever gets control.
Back to this line:

Code:
9F31    .WORD $9F1E


takes us here:

Code:
9F1E    .WORD $9F20
9F20    JMP    ($B004)


which takes us here:

Code:
B004    .WORD $B970


which takes us here:

Code:
B970    JSR     $BA31
B973    BNE     $B970
B975    RTS


back to the first p-code engine.

Code:
B976    .BYTE 1, $7C, $60       ; CALL $B97F    ;the stack swap routine
B979    .BYTE 4, $EB, $19       ; LDA $C0E8     ;turn off drive
B97C    .BYTE 8, $47, $69       ; JMP $B044     ;jump jump


to here:

Code:
B044    JMP    ($B00C)


and finally to here:

Code:
B00C    .WORD $800


What do we find at $800?

Code:
0800    JSR    $2204
0803    JSR    $3300


And at $3300?

Code:
3300    LDA    #5
3302    STA    $3313
3305    LDA    #$30
3307    STA    $3310
330A    LDY    #3
330C    LDX    #0
330E    LDA    $3000,X
3311    STA    $500,X
3314    INX
3315    BNE    $330E
3317    INC    $3310
331A    INC    $3313
331D    DEY
331E    BNE    $330E
3320    RTS


That flash of garbage on the text screen is the result of this routine.
And at $3000? I'll show only three lines:

Code:
3000    JMP    $562
...
3062    JSR    $662
3065    JMP    $562


Yes, it's the same code as the one at $AD00.
However, if this one fails, it jumps to a memory location containing an illegal instruction which reboots the machine.
How does $500 get control? It's very sneaky.

Code:
0864    JSR    $4D34
0867    STA    $C99
...
4D34    PLA
4D35    STA    $4E
4D37    PLA
4D38    STA    $4F      ;fetch $0866 into $4E-4F
4D3A    LDA    $4E
4D3C    CLC
4D3D    ADC    #3       ;skip over fake STA instruction
4D3F    TAY
4D40    LDA    $4F
4D42    ADC    #0
4D44    PHA
4D45    TYA
4D46    PHA             ;push that address back to the stack
4D47    LDA    $4F
4D49    LDY    #3
4D4B    EOR    ($4E),Y  ;fetch #$0C and change it to #$04
4D4D    PHA
4D4E    LDA    $4E
4D50    DEY
4D51    EOR    ($4E),Y  ;fetch #$99 and change it to #$FF
4D53    PHA
4D54    RTS


That's our $500. We can't disable the JSR because it will corrupt $C99 which is part of the keyboard handler.
How about we NOP the last two PHA instead? That's a great idea, if only we could find those bytes.
Here's the problem:

Code:
9D16    CLC
9D17    LDY    #0
9D19    LDA    $9CC2
9D1C    EOR    ($73),Y
9D1E    STA    ($73),Y
9D20    LDA    $9CBE
9D23    RRA    $9CC2
9D26    STA    $9CC2
9D29    DEY
9D2A    BNE    $9D19


Everything is encoded. Also, that RRA instruction isn't executed. It's modified by this p-code:

Code:
9D75    .WORD xpush_imm
9D77    .WORD $FFFE             ;PUSH #$FFFE
9D79    .WORD xpush_imm
9D7B    .WORD $9D16             ;PUSH $9D16
9D7D    .WORD xpush_imm
9D7F    .WORD $D
9D81    .WORD xadd_stk_pop      ;$9D16+#$0D = $9D23
9D83    .WORD xadd_mem_stk      ;change $9D16 from #$6F to #$6D


Fortunately, the encryption key doesn't depend on the values that were decoded, so we can perform pinpoint alterations.
We know the value before (#$ED and #$53) and after (#$48 and #$48), so we can determine the key (#$A5 and #$1B), and determine the replacement values (#$4F and #$F1).

T07 S02 A4D, ED -> 4F
T07 S02 A53, 53 -> F1

Now we're done.
Revenir en haut de page
Voir le profil de l'utilisateur Envoyer un message privé Visiter le site web de l'utilisateur
toinet
Site Admin


Inscrit le: 15 Juin 2007
Messages: 2949
Localisation: Le Chesnay, France

MessagePosté le: Sam 12 Sep 2015, 11:59    Sujet du message: Répondre en citant

he he, welcome to the world of EA's p-code.
What I like with it is that the interpreter has evolved over the years in the number and features of each command.

One advice (Skyfox powa): play the game and check that there are no other protection checks.

Antoine
Revenir en haut de page
Voir le profil de l'utilisateur Envoyer un message privé Visiter le site web de l'utilisateur
qkumba



Inscrit le: 29 Jan 2012
Messages: 176

MessagePosté le: Sam 12 Sep 2015, 19:13    Sujet du message: Répondre en citant

toinet a écrit:
he he, welcome to the world of EA's p-code.


The stack-based p-code is like nothing that they've done before or since, though. Marble Madness didn't have it, Archon didn't have it, Last Gladiator didn't have it... Only the first layer p-code is common to them all.

toinet a écrit:
One advice (Skyfox powa): play the game and check that there are no other protection checks.


The Skyfox cloud crash wasn't really a protection check, it was trying to read in data using the original loader.
However, Mack is fully playable. I completed all three levels successfully.
Revenir en haut de page
Voir le profil de l'utilisateur Envoyer un message privé Visiter le site web de l'utilisateur
Montrer les messages depuis:   
Poster un nouveau sujet   Répondre au sujet     Index du Forum -> PROTECTION MALEFIQUE Toutes les heures sont au format GMT + 1 Heure
Page 1 sur 1

 
Sauter vers:  
Vous ne pouvez pas poster de nouveaux sujets dans ce forum
Vous ne pouvez pas répondre aux sujets dans ce forum
Vous ne pouvez pas éditer vos messages dans ce forum
Vous ne pouvez pas supprimer vos messages dans ce forum
Vous ne pouvez pas voter dans les sondages de ce forum


Powered by phpBB © 2001, 2005 phpBB Group
Traduction par : phpBB-fr.com