Game Doctor SF3 - BIOS Exploit

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
Post Reply
neviksti
Posts: 205
Joined: Thu Jun 22, 2006 11:39 pm

Game Doctor SF3 - BIOS Exploit

Post by neviksti » Tue Jul 01, 2008 1:43 am

I finally found out how to trick the SF3 into letting a program running on the copier have access to all the copier memory and registers. The SF7 "super mode" injected code directly by injecting a brk statement, and then switching the memory mapping to "bios mode" temporarily. Instead the SF3 inserts code at every NMI.

I tried my trick from before of setting the stack to a carefully chosen invalid location and it didn't work. Also I read the NMI vector and nothing happenned. So how does it know when to switch to bios mode?

Well it is amazingly specific. It looks to see 4 decreasing accesses to $00:0000-1FFF and then the NMI vector. Normal code running on the processor can't cause this to happen, only an interrupt.

However, a carefully chosen sequence of DMA can do it. Run this code (obviously best done from RAM) and you'll switch to "BIOS" mode.

Code: Select all

	LDY #$3e90
	STY $4300		; (B) PPU -> (A) CPU, auto decrement, read 1 reg, $213e (ppu status flag)
	LDY #$1F00
	STY $4302		; (A) offset
	LDY #$0004
	STY $4305		; number of bytes to transfer
	LDA #$00
	STA $4304		; bank address = $00

	LDY #$3e00
	STY $4310		; (A) CPU -> (B) PPU, auto increment, write 1 reg, $213e (ppu status flag)
	LDY #$FFEA
	STY $4312		; (A) offset (native mode NMI vector)
	LDY #$0002
	STY $4315		; number of bytes to transfer
	LDA #$00
	STA $4314		; bank address = $00

	LDA #$03
	STA $420B		;start DMA transfer
I haven't looked around much yet. But here's first impressions:
bank $00:
..$8000-$8FFF appears to be the copier registers for $40 then repeated constantly.
..$9000-$FFFF ROM
bank $01-03: ROM
bank $04: ?? some memory
bank $05: ?? same memory as $04 ??
bank $06: probably copier SRAM
bank $07: probably DRAM

Everything else seems unmapped.
Some registers probably control mapping in of the external cartridge for dumping reasons.

EDIT: Here's an example of the exploit with a memory viewer, so you can play with the copier registers. Let me know what you find out.

http://neviksti.com/SNES/SF2exp3

tepples
Posts: 21985
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Game Doctor SF3 - BIOS Exploit

Post by tepples » Tue Jul 01, 2008 2:49 pm

neviksti wrote:So how does it know when to switch to bios mode?

Well it is amazingly specific. It looks to see 4 decreasing accesses to $00:0000-1FFF and then the NMI vector.
I seem to remember some topic on this board about ways to expand interrupt handling on the NES in which someone suggested something similar. Can anyone dig it up?

kammedo
Posts: 57
Joined: Wed May 28, 2008 5:43 am

Post by kammedo » Thu Jul 03, 2008 2:53 am

Do you still have the information about the GDSF7 exploit?
It would be intresting and helpfull to have it..

neviksti
Posts: 205
Joined: Thu Jun 22, 2006 11:39 pm

Post by neviksti » Thu Jul 03, 2008 11:45 pm

Sure I guess.
Here's a chunk from the SF7 code which explains everything

Code: Select all

exploit_setup:
	; exploit code will actually be run from WRAM
	; to get a "real time save" routine to initiate, we need to:
	;     1] ..wait..
	;           It appears that there is some kind of timer in the SF7.
	;           It only allows the copier to interrupt the flow of code roughly
	;           once per VBlank.  _Also_, it requires a little time before the
	;           very first "saver routine" interrupt.  This confused me quite
	;           a bit at first because removing code that didn't seem to matter
	;           would cause the exploit to fail.  Just keep it in mind.
	;     2a] write #$01 to $4016
	;           or
	;     2b] read $4219

	;move code to WRAM
	ldx #ExploitCode
	ldy #$0000
-	lda.w $8000,X		;Address adjusted
	sta.w $0000,Y
	inx
	iny
	cpx #ExploitCodeEnd
	bne -


	;----- try to goto $00:0001
	ldx #$B030	;Setup the stack to a carefully chosen "invalid" value
	txs

	lda #$01
	sta $4016	;dummy write to trigger a SF7 "save state feature routine" 
			;   the invalid stack makes the bank value wrong (code does a PHA / PLB)
			;   so joypad is read incorrectly (accessing ROM instead of SF7 RAM)
			;   it thinks the user requested something (regardless of actual joypad presses)
			;   it starts into some routines, at the first return it "pulls" the
			;      return address from ROM because of where the stack is pointed
			;   thus, it ends up at our code in WRAM
			;     (if you don't trick the SF7 joypad read, it will return to the wrong place
			;      due to the incorrect stack ... but it will return in cartridge mode)

	;jmp.l $000001 ;--test in emulators
NOTE: I am really not sure if the execution is redirected to $00:0000 or $00:0001 as the code comments claim. I always set many instructions at the beginning of $0000 to NOP just in case.

kammedo
Posts: 57
Joined: Wed May 28, 2008 5:43 am

Post by kammedo » Fri Jul 04, 2008 1:05 am

neviksti wrote:Sure I guess.
Here's a chunk from the SF7 code which explains everything

Code: Select all

exploit_setup:
	; exploit code will actually be run from WRAM
	; to get a "real time save" routine to initiate, we need to:
	;     1] ..wait..
	;           It appears that there is some kind of timer in the SF7.
	;           It only allows the copier to interrupt the flow of code roughly
	;           once per VBlank.  _Also_, it requires a little time before the
	;           very first "saver routine" interrupt.  This confused me quite
	;           a bit at first because removing code that didn't seem to matter
	;           would cause the exploit to fail.  Just keep it in mind.
	;     2a] write #$01 to $4016
	;           or
	;     2b] read $4219

	;move code to WRAM
	ldx #ExploitCode
	ldy #$0000
-	lda.w $8000,X		;Address adjusted
	sta.w $0000,Y
	inx
	iny
	cpx #ExploitCodeEnd
	bne -


	;----- try to goto $00:0001
	ldx #$B030	;Setup the stack to a carefully chosen "invalid" value
	txs

	lda #$01
	sta $4016	;dummy write to trigger a SF7 "save state feature routine" 
			;   the invalid stack makes the bank value wrong (code does a PHA / PLB)
			;   so joypad is read incorrectly (accessing ROM instead of SF7 RAM)
			;   it thinks the user requested something (regardless of actual joypad presses)
			;   it starts into some routines, at the first return it "pulls" the
			;      return address from ROM because of where the stack is pointed
			;   thus, it ends up at our code in WRAM
			;     (if you don't trick the SF7 joypad read, it will return to the wrong place
			;      due to the incorrect stack ... but it will return in cartridge mode)

	;jmp.l $000001 ;--test in emulators
NOTE: I am really not sure if the execution is redirected to $00:0000 or $00:0001 as the code comments claim. I always set many instructions at the beginning of $0000 to NOP just in case.
Oh, good old stack manipulations ;)
Thanks neviksti, I'll try to see if this can get the setup running. We definitely need to talk more about the GDSF7, I would be intrested in exploring more of its internals! I don't know if you remember, but I emailed you about that as well.

EDIT : so I guess the way of working is something like that :

a) you trigger a pad crossing
b) on the VBlank, the GDSF7 checks the pads and does a PLB from the stack, but being the stack pointed to *your* position, it pops *your* address from it and consequently jumps to your location.

Correct?

EDIT 2 :

suppose I get the reset vector of FEoEZ (which is easy) and set up a program to switch there - should work, shouldnt it?

neviksti
Posts: 205
Joined: Thu Jun 22, 2006 11:39 pm

Post by neviksti » Fri Jul 04, 2008 1:31 am

kammedo wrote:Thanks neviksti, I'll try to see if this can get the setup running.
?? What exactly is wrong with your setup? I don't see how this would help fix anything.
kammedo wrote:EDIT : so I guess the way of working is something like that :

a) you trigger a pad crossing
b) on the VBlank, the GDSF7 checks the pads and does a PLB from the stack, but being the stack pointed to *your* position, it pops *your* address from it and consequently jumps to your location.

Correct?
I'm not sure what you mean by "a)", and as for "b" vblank is not involved (nmi is not even activated here) and the order you wrote is not quite right.

When SF7 sees the joypads being read, it literally inserts code by replying to the next opcode requests with a BRK command... at this point it is in "bios mode". So the interrupt vector is taken from the bios code and the interrupt code in the bios is executed. One of the first things it does is lda #$06 / pha / plb in order to make the databank $06 which is the copier's SRAM. It doesn't want to mess with any of the SNES memory so as not to mess with the running game it interrupted... therefore it uses its SRAM for everything -> including where it stores the joypad data it reads in. It then checks the stored joypad data, but since it isn't actually reading RAM, it doesn't get the joypad data but some data in the ROM. It so happens this causes it to execute some routine. To return from the routine it tries to execute RTS, and this pulls an address from the "stack" (actually data in the ROM) and ends up transferring execution to my code in RAM.

Make sense?


EDIT:
kammedo wrote:EDIT 2 :

suppose I get the reset vector of FEoEZ (which is easy) and set up a program to switch there - should work, shouldnt it?
I'm sorry. I'm not understanding what you are trying to do at all.

The exploit is not needed to do any of the SPC7110 dumping. All the dumps so far were done on the SF3 before I figured out this exploit.

kammedo
Posts: 57
Joined: Wed May 28, 2008 5:43 am

Post by kammedo » Fri Jul 04, 2008 1:43 am

neviksti wrote: When SF7 sees the joypads being read, it literally inserts code by replying to the next opcode requests with a BRK command... at this point it is in "bios mode". So the interrupt vector is taken from the bios code and the interrupt code in the bios is executed. One of the first things it does is lda #$06 / pha / plb in order to make the databank $06 which is the copier's SRAM. It doesn't want to mess with any of the SNES memory so as not to mess with the running game it interrupted... therefore it uses its SRAM for everything -> including where it stores the joypad data it reads in. It then checks the stored joypad data, but since it isn't actually reading RAM, it doesn't get the joypad data but some data in the ROM. It so happens this causes it to execute some routine. To return from the routine it tries to execute RTS, and this pulls an address from the "stack" (actually data in the ROM) and ends up transferring execution to my code in RAM.
Sure, clear as the sky.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Fri Jul 04, 2008 11:19 am

neviksti wrote:One of the first things it does is lda #$06 / pha / plb in order to make the databank $06 which is the copier's SRAM. It doesn't want to mess with any of the SNES memory so as not to mess with the running game it interrupted...
(tangent) Doesn't the PHA modify RAM in the SNES in a way that the game could be affected by?

neviksti
Posts: 205
Joined: Thu Jun 22, 2006 11:39 pm

Post by neviksti » Fri Jul 04, 2008 12:28 pm

blargg wrote:(tangent) Doesn't the PHA modify RAM in the SNES in a way that the game could be affected by?
Yes it is possible to detect, however I don't know of any instruction that changes the databank except PLB.

The only way they could have fixed it would be to map some SRAM into bank 0 and use that for their temporary stack. The copier already has the capability to do this, so it could indeed be avoided. But the assumption that there is room on the stack is a reasonable assumption... rarely if ever would a programmer store data literally right below the stack.

EDIT: Just remembered, MVN and MVP also affect the databank register.

Post Reply