Game Doctor SF3 - BIOS Exploit

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Game Doctor SF3 - BIOS Exploit
by on (#34569)
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:
   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
Re: Game Doctor SF3 - BIOS Exploit
by on (#34586)
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?

by on (#34603)
Do you still have the information about the GDSF7 exploit?
It would be intresting and helpfull to have it..

by on (#34617)
Sure I guess.
Here's a chunk from the SF7 code which explains everything
Code:
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.

by on (#34619)
neviksti wrote:
Sure I guess.
Here's a chunk from the SF7 code which explains everything
Code:
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?

by on (#34621)
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.

by on (#34623)
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.

by on (#34630)
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?

by on (#34632)
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.