18/07/2011

Super nain tant dos

Introduction

Après le reverse de Aladdin dans le dernier post, j'ai voulu jouer avec un autre jeux, Zombies :

zombie_jaq.png

Je crois que ce jeu quand j'étais petit je n'ai jamais réussi à le finir en entier, il est super difficile, mais super fun.

Passwords

Comme dans le jeu Aladdin, tous les 5 levels, on nous file un password pour pouvoir continuer là où on s'est arrêté :

zombie_menu.png

Afin de trouver facilement la routine de vérification des passwords, il m'en fallait un valide, comme ca en tracant le code, je la trouverais facilement, je suis donc allé jusqu'au level 5 :

zombie_passwd.png

Sayez je suis en mesure de pouvoir loguer et meme voir les password que je rentre au sein du debugger :

zombie_w4kf.png

Voici la routine commenté :

$82/B018 AD A0 1E    LDA $1EA0  [$82:1EA0]
$82/B01B C9 42 43    CMP #$4342              ====\
$82/B01E D0 0D       BNE $0D    [$B02D]          |
$82/B020 AD A2 1E    LDA $1EA2  [$82:1EA2]       |
$82/B023 C9 44 46    CMP #$4644                  |  SECRET LEVEL == "BCDF"
$82/B026 D0 05       BNE $05    [$B02D]     =====|
$82/B028 9C 7C 1E    STZ $1E7C  [$82:1E7C]       |
$82/B02B 18          CLC                         |
$82/B02C 6B          RTL                /*RET*/  |
$82/B02D E2 30       SEP #$30               <====/
$82/B02F AE A0 1E    LDX $1EA0  [$82:1EA0]   X = *(0x1EA0)
$82/B032 AD A2 1E    LDA $1EA2  [$82:1EA2]   A = *(0x1EA2)
$82/B035 8D A0 1E    STA $1EA0  [$82:1EA0]   *(0x1EA0) = A
$82/B038 8E A2 1E    STX $1EA2  [$82:1EA2]   *(0x1EA2) = X
$82/B03B C2 30       REP #$30                
$82/B03D A2 00 00    LDX #$0000              X = 0
$82/B040 A9 05 00    LDA #$0005              A = 5
$82/B043 8D 7C 1E    STA $1E7C  [$82:1E7C]   *(0x1E7C) = A 
$82/B046 E2 30       SEP #$30                <======================================\
$82/B048 BC 4B B1    LDY $B14B,x[$82:CC15]   Y = *(0xB14B + x)                       |
$82/B04B B9 78 B1    LDA $B178,y[$82:B17E]   A = *(0xB178 + y)                       |
$82/B04E EB          XBA                     Exchange byte (ex : 4243 -> 43 42)      |
$82/B04F BC 4A B1    LDY $B14A,x[$82:CC14]   Y = *(0xB14A + x)                       |
$82/B052 B9 78 B1    LDA $B178,y[$82:B17E]   A = *(0xB178 + y)                       |
$82/B055 C2 30       REP #$30                                                        |
$82/B057 CD A0 1E    CMP $1EA0  [$82:1EA0]                                           |
$82/B05A F0 1B       BEQ $1B    [$B077]      === First stage Ok =====\               |
$82/B05C EE 7C 1E    INC $1E7C  [$82:1E7C]   \                       |               |
$82/B05F EE 7C 1E    INC $1E7C  [$82:1E7C]   |                       |               |
$82/B062 EE 7C 1E    INC $1E7C  [$82:1E7C]   |--> 0x1E7C += 4        |               |
$82/B065 EE 7C 1E    INC $1E7C  [$82:1E7C]   /                       |               |
$82/B068 E8          INX                     X++;                    |               |
$82/B069 E8          INX                     X++;                    |               | 
$82/B06A E0 1A 00    CPX #$001A              if (A == 0x1A)          |               |
$82/B06D D0 D7       BNE $D7    [$B046]      ========================|===============/
$82/B06F A9 01 00    LDA #$0001                                      |
$82/B072 8D 7C 1E    STA $1E7C  [$82:1E7C]                           |
$82/B075 38          SEC                                             |
$82/B076 6B          RTL                     /* RET == Failed */     |
$82/B077 8E 38 00    STX $0038  [$82:0038]   <=======================|
$82/B07A A2 00 00    LDX #$0000              X = 0
$82/B07D A9 01 00    LDA #$0001              A = 1
$82/B080 8D 3A 00    STA $003A  [$82:003A]   *(0x3A) = A
$82/B083 E2 30       SEP #$30                <======================================\
$82/B085 AC 38 00    LDY $0038  [$82:0038]   Y = *(0x38)                             |
$82/B088 BD 65 B1    LDA $B165,x[$82:CC2F]   A = *(0xB165 + x)                       |
$82/B08B 18          CLC                                                             |
$82/B08C 79 8E B1    ADC $B18E,y[$82:B194]   A += *(0xB18E + y) & 0xff               |
$82/B08F A8          TAY                     Y = A                                   |
$82/B090 B9 78 B1    LDA $B178,y[$82:B17E]   A = *(0xB178 + y)                       |
$82/B093 EB          XBA                     Exchange byte (ex : 4243 -> 43 42)      |
$82/B094 AC 38 00    LDY $0038  [$82:0038]   Y = *(0x38)                             |
$82/B097 BD 64 B1    LDA $B164,x[$82:CC2E]   A = *(0xB164 + x)                       |
$82/B09A 18          CLC                                                             |
$82/B09B 79 8D B1    ADC $B18D,y[$82:B193]   A += *(0xB18D + y) & 0xff               |
$82/B09E A8          TAY                     Y = A                                   |
$82/B09F B9 78 B1    LDA $B178,y[$82:B17E]   A = *(0xB17E + y)                       |
$82/B0A2 C2 30       REP #$30                                                        |
$82/B0A4 CD A2 1E    CMP $1EA2  [$82:1EA2]                                           |
$82/B0A7 F0 12       BEQ $12    [$B0BB]      === JUMP GOOD BOY ===                   |
$82/B0A9 EE 3A 00    INC $003A  [$80:003A]                                           |
$82/B0AC E8          INX                     x++                                     |
$82/B0AD E8          INX                     x++                                     |
$82/B0AE E0 14 00    CPX #$0014              if (x != 0x14)                          |
$82/B0B1 D0 D0       BNE $D0    [$B083]      =======================================/
$82/B0B3 A9 01 00    LDA #$0001              
$82/B0B6 8D 7C 1E    STA $1E7C  [$80:1E7C]
$82/B0B9 38          SEC                  
$82/B0BA 6B          RTL                  /* RET = Failed Password */

Elle peut paraitre un peu longue mais en fait il y a une subtilité marrante, pour chaque password, il y en a 10 de possible, chacun d'eux donnera un nombre de victimes différents lorsque vous partirez du niveau désiré.
J'ai ecrit un petit code en python pour les générer:

stage = "HNCVWBXKGZTDLFPSRYJMQ"
B14A_offset =    [0x10, 0x00, 0x08, 0x05, 0x02,
                  0x0D, 0x11, 0x07, 0x05, 0x06,
                  0x0C, 0x11, 0x09, 0x0C, 0x14,
                  0x12, 0x03, 0x09, 0x0E, 0x10,
                  0x00, 0x0C, 0x12, 0x04, 0x0F,
                  0x0B, 0x04, 0x07]

B18D_offset = [0x00, 0xFE, 0xFE, 0xFF, 0x02,
                  0x00, 0x01, 0x02, 0xFF, 0xFE,
                  0x02, 0x02, 0x02, 0x01, 0x00,
                  0x00, 0x01, 0x01, 0x01, 0x00,
                  0xFF, 0xFF, 0x00, 0xFF, 0x00,
                  0x01, 0x18, 0x69, 0x8F]

B164_offset =    [0x04, 0x07, 0x0F, 0x0A, 0x0C,
                  0x12, 0x10, 0x09, 0x0E, 0x10,
                  0x03, 0x05, 0x0D, 0x08, 0x08,
                  0x0C, 0x0A, 0x0E, 0x06, 0x11]

x = 0
y = 0
print "Nb victims \t 1\t 2\t 3\t 4\t 5\t 6\t 7\t 8\t 9\t 10"
while (x < 0x1a):
    second = stage[B14A_offset[x + 1]]
    third = stage[B14A_offset[x]]
    y = 0
    lvl = "Lvl X\t\t"
    while (y < 0x14):
        a = B164_offset[y + 1]
        a += B18D_offset[x + 1]
        a &= 0xff
        fourth = stage[a]
        a = B164_offset[y]
        a += B18D_offset[x]
        a &= 0xff
        first = stage[a]
        lvl += first + second + third + fourth + "\t"
        y += 2
    print lvl
    x += 2
print "Secret Level : BCDF"

Voici l'output :

Nb victims 1	  2	  3	  4	  5	  6	  7	  8	  9	 10
Lvl X	WHRB	 SHRG	 LHRR	 RHRK	 PHRP	 VHRV	 FHRX	 GHRT	 THRL	XHRS	
Lvl X	CBGX	 FBGZ	 TBGY	 PBGG	 LBGS	 NBGW	 DBGK	 XBGD	 GBGF	WBGR	
Lvl X	XFCK	 YFCT	 PFCJ	 JFCZ	 RFCR	 BFCB	 SFCG	 TFCL	 LFCP	GFCY	
Lvl X	BKYZ	 RKYL	 FKYQ	 YKYD	 SKYJ	 WKYK	 PKYT	 ZKYP	 DKYR	KKYM 
Lvl X	VXBB	 PXBG	 DXBR	 SXBK	 FXBP	 CXBV	 LXBX	 KXBT	 ZXBL   BXBS 
Lvl X	XYLZ	 YYLL	 PYLQ	 JYLD	 RYLJ	 BYLK	 SYLT	 TYLP    LYLR   GYLM 
Lvl X	XLZG	 YLZD	 PLZM	 JLZT	 RLZY	 BLZX	 SLZZ    TLZF    LLZS   GLZJ 
Lvl X	WJQK	 SJQT	 LJQJ	 RJQZ	 PJQR	 VJQB    FJQG    GJQL    TJQP   XJQY 
Lvl X	BZVG	 RZVD	 FZVM	 YZVT	 SZVY    WZVX    PZVZ    ZZVF    DZVS   KZVJ 
Lvl X	BRPK	 RRPT	 FRPJ	 YRPZ    SRPR    WRPB    PRPG    ZRPL    DRPP   KRPY 
Lvl X	VLHX	 PLHZ	 DLHY    SLHG    FLHS    CLHW    LLHK    KLHD    ZLHF   BLHR 
Lvl X	WWJX	 SWJZ    LWJY    RWJG    PWJS    VWJW    FWJK    GWJD    TWJF	XWJR 
Lvl X	WDSG     SDSD    LDSM    RDST    PDSY    VDSX    FDSZ    GDSF	 TDSS   XDSJ 
Secret Level : BCDF

On teste le secret level and it works :

zombie_bonus.png

Tous les autres passwords sont aussi fonctionnels, Have fun :]

18/07/2011

Retour en enfance

Introduction

C'est en voyant des vidéos du site le joueur du grenier où le mec délire à finir un paquet de jeux old-school, ou encore voir des speed run sur speeddemosarchive, qu'une envie de ressortir les vieilles consoles prend le dessus.
C'est pour ça que je décide de m'amuser avec une vieille rom d'un jeu que j'ai pas mal apprécié, Aladdin :

aladdin_front.jpg

C'est l'occasion aussi de tester ce que vaut un tracer/debugger pour SNES.

Passwords

Dans le jeu Aladdin, tous les 2 - 3 levels, on nous donne un password à rentrer pour pouvoir revenir là où on en était la dernièere fois :

aladdin_password

Chaque password est une combinaison de 6 personnages du jeu :

J'étaits en mesure de pouvoir tracer le programme avec cette outil :

aladdin_snes

A partir de là le logiciel me crée des fichiers de logs : Aladdin0000.log, et en sortant les couteaux suisses : sort, uniq, cut en loguant quand le password est valide ou non, je fus en mesure de trouver la routine de vérification des password :

$84/ADE4 A0 00       LDY #$00
$84/ADE6 B6 A6       LDX $A6,y  [$00:00A6]
$84/ADE8 8A          TXA
$84/ADE9 D1 AA       CMP ($AA),y[$00:5555]
$84/ADEB F0 18       BEQ $18    [$AE05]
$84/ADED C2 21       REP #$21
$84/ADEF A5 AA       LDA $AA    [$00:00AA]
$84/ADF1 69 04 00    ADC #$0004
$84/ADF4 85 AA       STA $AA    [$00:00AA]
$84/ADF6 E2 20       SEP #$20
$84/ADF8 EE 0C 00    INC $000C  [$00:000C]
$84/ADFB AD 0C 00    LDA $000C  [$00:000C]
$84/ADFE C9 08       CMP #$08
$84/AE00 D0 E2       BNE $E2    [$ADE4]
$84/AE02 4C 7B AE    JMP $AE7B  [$00:AE7B]
$84/AE05 C8          INY
$84/AE06 C0 04       CPY #$04
$84/AE08 D0 DC       BNE $DC    [$ADE6]
$84/AE0A AD 0C 00    LDA $000C  [$00:000C]
$84/AE0D AA          TAX
$84/AE0E BD D6 FD    LDA $FDD6,x[$00:FDD6]
$84/AE11 8D 05 00    STA $0005  [$00:0005]
$84/AE14 8D 5C 03    STA $035C  [$00:035C]
$84/AE17 9C 5D 03    STZ $035D  [$00:035D]
$84/AE1A 22 0A 81 80 JSL $80810A[$80:810A]
$84/AE1E 4C 71 AE    JMP $AE71  [$00:AE71]
$84/AE21 89 08       BIT #$08
$84/AE23 F0 0E       BEQ $0E    [$AE33]
$84/AE25 A6 A5       LDX $A5    [$00:00A5]

Bon j'ai du apprendre vite fait l'assembleur du cpu de la SNES : 65c816, afin de comprendre comment retrouver tous les passwords sans trop de difficulté.

aladdin_val_pass

C'est à l'adresse 0xA6 que nos choix de password sont stockés, on voit meme à quelle valeur correspond tel personnage :

Revenons sur le code précédent, je vais le réecrire en pseudo-code pour ce qui ont du mal :

int	*pass_valid = 0xFD75;
cpt = 0;
begin:
y = 0;
test:
a = input[y]; /* (0xA6) */
if (a == *(pass_valid + y))
	y++;
	if (y != 4)
		goto test;
	else
		goto pass_valid;
pass_valid += 4;
cpt += 1;
if (cpt != 8)
	goto begin;
else
	/* Password failed : Restart du jeu */

En fait il y a 7 passwords possible qu'on va gentiment dumper :

aladdin_dump

Attention ils se lisent à l'envers ;) :

Voilà on a tous les passwords, bon on s'est quand meme bien fait chier car suffisait de google : "Password Aladdin SNES".
Mais bon le but c'était de s'amuser :]

aladdin_completed

Pages : 1