# Hack Protection in 1992

Posted by Tursi, 26 December 2014 · 63 views
I did some further study on the Thunder Force IV lockup - I found to my pleasant surprise that it reproduced on my hacked version even without the menu. So I was able to dig a bit deeper.
I found that it came down to the branch I had randomly selected on the SEGA screen - the vector table code was okay, but changing those two bytes caused the hang. (After a handful of runs it finally occurred to me that a save state would save me a lot of time).
Eventually, I managed to get REGEN to breakpoint at the critical moment for me --- and what did I find but a checksum routine! It was called when I collected the 1-Up.
The code appeared to be located at $5CAE, and looked like this:

     lea $100.w,a1		start with address $100
     adda.l a1,a1		double address ($200 - normal entry point)
     moveq #$0,d0		zero accumulator
     move.w #$2b7f,d7	count of $2b80 dwords - 44,544 bytes ($0200 - $AFFF)
    lp:
     add.l (A1)+,d0		update checksum
     dbra d7,lp		count down
     eori.l #$454d4f,d0	magic value, I presume to obfuscate, XOR'd
     cmp.l $15ff8.l,d0	test against fixed value ($97bef5c5)
     bne $1d26		branch to failure code (does a BSR to a STOP #$2500 with no valid int configured)
     rts			back to caller

I was able to step through this code and get my checksum - $36beedf9 - but it made me worried that there may be other tests in the code that take a long time to find (for instance, maybe some endings lock up.)
So I wondered instead of hacking the value, maybe I could instead change where the menu tests it. If that is beyond $AFFF, likely other checksum code, if there is any, would leave it alone.
I set a read breakpoint on $FFF079 in REGEN (after entering the menu, since breakpoints in this emulator are buggy.) I confirmed other options didn't test the byte, then selected the music line. The breakpoint again did not trigger, to my disappointment. After a couple of quick tries, I found I had to set $FFF078, that worked. From there I analyzed the code that triggered it. It triggered at $FF20, so I dumped $FF00 - $FF24. that gave me this code, which was what I was after, at $FF1A:

     moveq #$32,D3		set upper limit
     tst.w $f078.w		test word at $f078 for zero
     beq l1			jump ahead if zero
     moveq #$47,d3		not zero, so set higher limit (BCD, it appears)
    l1:
     cmp.b #$99,d1		some unrelated check...

That tells me both that a whole word was used for the game-finished flag (heh), and where the code is. As it's above $AFFF, I'm going to move my hack here in hopes of avoiding any other hack detection code.
So first, going back to a pristine copy of TF4. Next, the easiest hack for the above code is to just NOP out the BEQ. At $FF20, replace "6702" with "4E71". This will make the code load $32, test $f078, then load $47 regardless of the result.
Now, of course, the boot checksum will be wrong again, so we set a breakpoint for $364, load the new code, and check the result. At $018E, replace "D9F6" with "C165".
Retesting showed that this fixed the lockup, but it would be a good idea to run through the whole game still and make sure nothing else breaks. (I'm partly tempted to kill that checksum routine too, as it induces a slight hiccup when collecting a 1-up. I always wondered about that - I thought it was just loading the voice -- but messing around with code designed to protect code from hacks is always a minefield - keep changes as small as possible or be prepared to spend a LOT of time. ;) )
Anyway, that appears to have TF3 and TF4 working... TF2 will be trickier as it only fails under hardware. Possibly the register clears for TF3 will help it, since it did check one of them as well. I won't know till I get it on hardware.