Cracking the protection of Last Tempest. !!! PARTIAL SUCCESS !!!

Started by eidis, July 01, 2014, 05:59:28 PM

Previous topic - Next topic

eidis

 Ladies and Gentlemen,

Today's candidate is Last Tempest, another one of those "uncrackable" games.

Get it here:
http://nfggames.com/x68000/Uploads/LastTempest.zip

Original thread in Tokugawa Corporate Forums
http://fullmotionvideo.free.fr/phpBB3/viewtopic.php?f=2&t=1092

Here is what we know so far:
1) The main executable LT.X is ITA lzx compressed.
2) The protection could be somehow tied to the SaveData.LT and the game sets read only attribute for that file.
3) Delicius proposed values can not be applied to the binary (Offset #2A9A2  (4E B9 00 01 41 18)) because game modifies the data after loading it to the mentioned RAM address.
4) LastTempest.zip has the unpacked LT.X on on the second floppy and the game runs fine if Delicious instructions are applied.

Once again, Heeeeeelp !

Keep the scene alive !
Eidis
X68000 personal computer is called, "X68K" or "no good good" is called, is the PC that are loved by many people today.

lydux

Caius ask me about this game some times ago. I've started writing a decompressor but got bored...
Well, it's time to finish this work !

As you noticed, the executable image is modified once loaded. Not by the game, but by Human68K kernel !

Explanation :

The M68000 CPU has a flat memory model, 24bits (16MB) of addressing space where must resides the whole hardware. On the first 12MB is the main RAM.
Now, within the RAM resides all the software you are currently running : CPU vectors, bios variables, floppy or HDD bootloaders followed by the Human68K kernel, various drivers, COMMAND.X shell, etc..., and finally the game executable you are currently playing.

Of course, each machines is unique in its software side, so the start of a game loading address is different most of the time (if not on each game run). And the game itself must perform displacements, reading or writing within its own program space to reach a specific routine, variables or constants. These are performed by some assembler instructions like JUMP (JMP, JSR, ...), BRANCH (BRA, BRS, ...) or some MOVE call, followed by one or two absolute offsets.
However, the loading address is not known by the game as it's runtime specific. The only process who know this address is the executable loader : Human68K kernel.

The trick is to assume that the program will start everytime at address 0x000000, and build a list of offsets that point into the program code where absolute position occurs. These are called "relocations" and such table resides into every .X files.
Once the raw executable image loaded by the kernel, it will read the relocation table and patch the whole image by adding the base loading address to the 4 bytes value pointed by the relocation. Then start the program.

My x68000 toolchain is able to build such table and will also read them.


Back to The Last Tempest.

Here is the disassembly output of the interesting part :

human68k-objdump -D lt.x

...
   2a952: 48e7 fffe      moveml %d0-%fp,%sp@-
   2a956: 4eb9 0002 2240 jsr 0x22240
   2a95c: 4eb9 0001 4062 jsr 0x14062
   2a962: 4eb9 0001 4118 jsr 0x14118 <====
   2a968: 4cdf 7fff      moveml %sp@+,%d0-%fp
   2a96c: 4e75            rts
   2a96e: 48e7 fffe      moveml %d0-%fp,%sp@-
   2a972: 4e71            nop
   2a974: 0002 ba80      orib #-128,%d2
   2a978: 4cdf 7fff      moveml %sp@+,%d0-%fp
   2a97c: 4e75            rts
   2a97e: 48e7 fffe      moveml %d0-%fp,%sp@-
   2a982: 6100 1190      bsrw 0x2bb14
   2a986: 43fa 13b0      lea %pc@(0x2bd38),%a1
   2a98a: 6100 1448      bsrw 0x2bdd4
   2a98e: 6100 1444      bsrw 0x2bdd4
   2a992: 5a6c 0068      addqw #5,%a4@(104)
   2a996: 6100 143c      bsrw 0x2bdd4
   2a99a: 066c 000a 0068 addiw #10,%a4@(104)
...


As you can see, the jump to the protection check routine is here (0x2a962 : opcode 0x4eb9 = JSR = Jump to Sub Routine), but with a different target address (0x014118) which is the absolute address starting from 0x000000.

Reading the relocation table starting from this address :

human68k-objdump -r --start-address=0x2a952 lt.x

lt.x:     file format xfile

RELOCATION RECORDS FOR [.text]:
OFFSET   TYPE              VALUE
0002a958 R_68K_32          .text
0002a95e R_68K_32          .text
0002a964 R_68K_32          .text  <====
0002a974 R_68K_32          .text
0002a9ec R_68K_32          .text
...


There is a relocation at position 0x02a964 which point directly to the 4 bytes number 0x00014118. So once loaded, 0x00014118 will be added with the base loading address of the image.
That's why you get different values between raw view or disassembly and runtime execution within XM6 !


The crack :

Delicious's idea is to patch at runtime the 6 bytes jump instructions by 3 times 0x4e71 which is the opcode for the "NOP" instruction (NOP = No OPeration = just do nothing...). This is still valid for your idea Eidis.
But the 0x2a964 relocation entry also need to be removed from the table or the second and third 0x4e71 will be added to the base loading address at runtime resulting in a random M68000 instruction, the game will crash or bug.


How to proceed :
- Apply Delicious's patch at offset 0x02A9A2. Change :
     - 4E B9 00 01 41 18
     - to
     - 4E 71 4E 71 4E 71
- At offset 0x032D13 (relocation table), delete the next 2 bytes (06 00)
- Now, this same offset (0x032D13) should now point 10. Change it to 16
- Go back to the XFile header at offset 0x00001A, change 8A to 88.



Please note that I haven't tested at all, so tell me the result.

One final note : I see something very strange at address 0x2a972. There is a NOP in the disassembly instead of a JSR. Is that a previous patch from you Eidis ?


Hope all this technical stuff is clear... And happy hacking guys !  8)

caius

#2
Ah, finally the Master appeared again with all his immense knowledge .Vive le roi!  :)

P.S.
Another interesting game to be cracked is Bomber Man IMHO.It's fully HD installable but it requires a physical floppy in a drive to properly start (2HDSIM or other disk image simulator won't do the trick).I think it's a pretty easy protection to defeat, something related to DOS/BIOS calls/checks I presume.

eidis

#3
 Hi Lydux !

Glad to see you again ! You were absolutely right about 0x2a972 containing NOP. Somehow I managed to mix up the unpacked files. Terribly sorry for the caused inconvenience. The archive has been reuploaded with the correct, unmodified and unpacked LT.X and translated HUMAN.SYS so system errors will be shown in English. The link is the same, please redownload.

Thank you for explaining about the relocation tables. This answered the question how the computer knows where to branch or jump. I followed your instructions strictly by the book, with unmodified, unpacked LT.X, but unfortunately got the attached error. Got another error when trying to read patched LT.X relocation table: "human68k-objdump.EXE: lt.x: File truncated".

If I understand correctly, the crack involves going to offset 0x02A9A2, replacing the JSR command and its destination absolute address with NOP, removing references to this JSR from relocation table, and doing something with the file header.

Could you please explain how did you knew which address needs to be modified in relocation table and why was it necessary to modify the file header ?

Thank you once again for sharing your knowledge with us and sorry for asking such simple questions.

Keep the scene alive !
Eidis

X68000 personal computer is called, "X68K" or "no good good" is called, is the PC that are loved by many people today.

H68k

That would be great if Bomber Man could be made to fully work off of a hard disk.

Reading through all this stuff makes my head hurt... and that I wish I knew a lot more 68k assembler...

eidis

X68000 personal computer is called, "X68K" or "no good good" is called, is the PC that are loved by many people today.

lydux

I did never leave Caius ! It's just I do not have as much free time as before. But I often come here reading topics and just want to help a bit with this one. :)

Quote from: Eidis
Thank you for explaining about the relocation tables. This answered the question how the computer knows where to branch or jump. I followed your instructions strictly by the book, with unmodified, unpacked LT.X, but unfortunately got the attached error. Got another error when trying to read patched LT.X relocation table: "human68k-objdump.EXE: lt.x: File truncated".
Many operating systems use similar way to load dynamic executable code.
I might have say you mistake somewhere, but it seems the game code execute in some way... We clearly see the patched JSR opcode by a NOP, but the relocation is still here.
The preceding 2 instructions are also JSR.
From the original unrelocated program :


   2a956: 4eb9 0002 2240 jsr 0x22240
   2a95c: 4eb9 0001 4062 jsr 0x14062
   2a962: 4eb9 0001 4118 jsr 0x14118


Once relocated, according to your memory dump. We have :


  DD906:   4EB9 000D51F0 JSR 0x0D51F0
  DD90C:   4EB9 00179FC2 JSR 0x179FC2
  DD912:   4E71 NOP (patched)
  DD914:   000E0830 protection routine location


So, we can calculate the base loading address (first JSR) :
0xD51F0 - 0x22240 = 0xB2FB0
However, with the second and third JSR  :
0x179FC2 - 0x14062 = 0x165F60
0xE0830 - 0x12118 = 0xCE718

Something is wrong with the patched relocation... We should get the exact same base address. Or it may be cause by a self patch, which is highly possible as we are talking about protection ! I can see many self patching here and there at executable startup...

Quote from: Eidis
If I understand correctly, the crack involves going to offset 0x02A9A2, replacing the JSR command and its destination absolute address with NOP, removing references to this JSR from relocation table, and doing something with the file header.

Could you please explain how did you knew which address needs to be modified in relocation table and why was it necessary to modify the file header ?
Correct ! I will explain you all I know about the XFile executable file format later.


Until then, let's try something else : with a clean unpacked LT.X, at offset 0x2A9A2, try to change bytes 4EB9 to 6004. Just these two bytes ! No need to mess with the relocation table. 0x6004 is the opcode for a short branch to 4 bytes forward, so will skip the relocated offset and continue the execution flow from this point.

BTW Eidis, it seems to me you start learning and understand M68K assembler ! That's really great !  :D

eidis

 Hi Lydux !

Thank you for your kind words and swift reply. Do you remember when long time ago there was no YouTube and once a week our favorite TV shows went on air, the thrilling anticipation, waiting and then the inexpressible joy of seeing it ? I have the same feeling when reading and waiting for your posts. So much knowledge, so much valuable information and so well presented in an easy to understand manner !

I went to offset 0x2A9A2, changed bytes 4EB9 to 6004 and it worked ! The game now launches and is playable. I even made a hard disk version of if so any future cracking will be less difficult.

The creator of Last Tempest was very clever and made multiple protections. You successfully defeated the first one, however there is another one. The cursor in the main menu is invisible (please see attached pictures) so it is impossible to tell which menu item is selected. It appears when Exit is selected so I think it is another protection.

The LastTempest.zip, with the cracked LT.X, was reuploaded.

Hard disk version is available here:
http://nfggames.com/x68000/Uploads/LastTempest.Lzh

An now it is question time :)

Please forgive me for the simple questions which I am about to ask you.

1) What is the benefit of knowing  base loading address ?
2) How do you calculate position for a specific branch or jump address in relocation table and how to interpret its format ?
3) Why was it necessary to modify the header in the first example ?
4) Is it possible to execute the program and use Debuger and follow each command step by step in order to find the protection ? I tried using Debuger but could not figure out how this particular feature worked.

Thank you once again for your expertise !

Update #1: I think the protection is similar to that in Y2. The game is accessing both floppy drives slightly before displaying the main menu. Insert floppy disks in both drives to see it in action. It is possible that the game reads data from a non standard sector or location and then patches the main executable in ram with that information.

Update #2: The following text is taken from the games readme file:

This game has copy protection. The original FD must be in the drive when you execute the game for the first time after installation. Once you have inserted the floppy disk, it is no longer needed during the subsequent executions of the game.

savedata.lt file is created after installation when the game is executed for the first time. It features high scores and status of protect check result.

Keep the scene alive !
Eidis
X68000 personal computer is called, "X68K" or "no good good" is called, is the PC that are loved by many people today.

SuperDeadite

I think thats actually a glitch caused by the hack itself.  It breaks the score counter as well, which makes sense as the protection is tied to the score data file.

Still nice to finally play this one on the real deal.  :)

UD2

#9
Hi all,
I've managed to figure out what the game looks for in an original copy and have succeeded in getting the game to run. I've attached a utility to this post that will "bless" a floppy by writing a protection sector to it, yielding a disk that passes the security check just like a real one would.

This game was interesting to reverse engineer. There's a ton of really clever anti-reverse-engineering tactics in play that made this one quite troublesome. Still, it's hard to fight against someone with an emulator :-). On that note, XM6 Pro-68K doesn't emulate the copy protection properly, so I'll give alternate instructions for running the game in there.
Update: I spoke with the author of XM6 Pro-68K and he provided guidance on getting things working properly in his emulator. To do so, pad the normal disk A image to a total of 0x140000 bytes, then follow the instructions normally.

To make disks:
  • Write disks A and B to floppy. Make sure you use the uncracked images, not the cracked ones.
  • With disk A in drive 0, run BLESS.X to write the protection sector.
  • It's not strictly necessary, but I like to delete any existing SaveData.LT on disk A, since the protection is tied to it and we want to start fresh. It should just create new data, but better safe than sorry. It'll be in the LT folder.
If you just want to play off floppy, boot with disk A in drive 0 and disk B in drive 1 and that's all.

If you want to install to HDD, the game is somewhat picky, so follow these steps exactly:
  • Make a directory called "LT" in the root of your HDD.
  • Use COPYALL to copy the contents of the LT directories of disks A and B into this directory.
  • From disk A's SYS directory copy over any drivers you may be missing. For me, that was just ZMSC.X. You can stick this wherever, I just put it in LT. The game orginally shipped with HIOCS.X and I think most people already have that installed. It also needs FLOAT2.X, but you probably already have that resident anyway.
  • Make a run script.
Here's what the manual suggests (with their "E:" swapped out for my "A:"):
ZMSC.x -u -m -t16 -w0 -p334 -s¥LT¥ZMD_DATA¥EFFECT.ZMD -b¥LT¥ZMD_DATA¥TEMPEST.ZPD
set LAST_TEMPEST=¥LT¥IMAGE_DATA;¥LT¥ZMD_DATA;A:¥LT;
LT

If you don't already have HIOCS.X and FLOAT2.X resident, make sure to run those at the start of the script.

On first run from HDD, make sure disk A is in drive 0. The game will check the disk for protection and create a save file if it passes. Subsequent runs do not need the disk in the drive.

Let me know if you hit any issues. I had a crash on real hardware during attract mode that I couldn't reproduce under emulation, and I don't think it's protection-related, my X68K has been flaky with other games recently :-(.
Edit: the issue is definitely my X68000, it seems DemoR8.Dem was corrupted. Sure enough, I loaded that file from the FDD that has been giving me trouble recently. Re-copying those files from the other drive made the issue go away. I can now say I haven't seen a single issue with this installation method :-).

To run under an emulator that doesn't handle the protection properly:
  • Follow the steps above for whichever installation method you prefer.
  • Load the game with a breakpoint set right after the protection sector hash computation. The offset from the load address is 0x021fd4. If you booted off floppy, it should be 0xd4f84 after relocation.
  • Use the debugger to set D5=0x1bda and D6=0x80fc.

If you use the debugger technique while running off HDD in an emulator, the installation will work as if a real floppy was inserted and you won't need to do it again next time.

I plan to create a utility to generate savefiles so you can avoid the need for writing a disk or messing about in the debugger, but this should be enough to at least play the game!

I will make a follow-up post at some point to explain just some of the nastiness that this game does to hide its protection, but the actual protection itself is quite simple, it just needs track 78 of side 1 of disk A (in drive 0) to be formatted like the other sectors, and it needs sector 0 to contain values that hash to a specific value (that's what you're forcing to happen when you edit D5 and D6 above). Y2 has a much more involved floppy scheme, but it's much more poorly hidden, while this game uses a very simple protection scheme that is hidden very well, with lots of checks to ensure you haven't defused those checks.

One more thing: I tried hex-editing the first branch after the hash check and the game seemed to run just fine, but I'd be scared to rely on that. I have very little confidence that there isn't some mechanism that runs deeper into the game to detect that and flag a cracked copy.

Have fun!

UD2

I should also add that the game has explicit detection for Herosoft SCSI and SxSI. It should also be able to run off MO disc. Other than that though, you may have mixed luck. The SCSI code seems pretty picky about what it supports.

UD2

#11
It turns out running from MO doesn't work due to a bug in the game. It assumes _S_INQUIRY will always fill the buffer given and if it doesn't you end up with whatever crap is left in the buffer from the previous use. This means the hash checks will always fail since the end of the buffer is undefined as the MO driver doesn't use all the bytes requested. I've written a TSR that can work around this by making that call behave as the game expects and also can intercept the floppy _B_READDI calls to fake out the protection without a disk in the drive. This should fully crack the game, no further patching necessary.
Run with "-R" to remove the TSR and "-F" to install without floppy emulation. Running without any flags will install normally. Enjoy!