Pages: 1
Posted on 12-12-12, 05:56 pm (rev. 4 by  Dirbaio on 04-18-14, 05:19 pm)
Super Mario
( ͡° ͜ʖ ͡°)

Karma: 8939
Posts: 2990/4433
Since: 06-08-11

What is it?

So many people ask me how to lock your ROM so people can't open it and steal your levels, tilesets and other cool stuff.

The answer is: It's not possible to completely lock a ROM. If someone has your ROM, and the ROM can be played, they have the data in their hands. So in theory, "un-locking" the ROM is always possible.

But, fortunately, it's possible to make it as HARD as you want.

Basically, to "lock" a ROM:

First step is modifying the format of some important data in the ROM so NSMB Editor and other DS hacking tools can't open the ROM properly. Second step is making an ASM hack so that the ROM's code can read the "modified" format.

So let's do the thing!

One easy way of locking the ROM is modifying the FAT. All DS tools use it to find out where files are located, so if you screw it up, no tool will open your ROM.

One way of doing it is XOR encryption:
We'll do it the easy way. XOR every byte with a fixed byte. It's not "good" XOR encryption, from looking at the FAT in hex it will be obvious, and decrypting it is easy. But, it's a good start.

The code

Basically what we will do is hack a function named FSi_RomReadCallback.
This function is what the game code uses to read from ROM. Everythign that is read from ROM will go through this function. So if we make it modify the data being read we can "decrypt" the FAT when the game wants to read it. This way the game will "see" the decrypted FAT when it reads it, even though it's encrypted on ROM, and will work properly.

The function from the game's code looks like this (when translated back to C):

int FSi_RomReadCallback(void* archPtr,  u8* dest, const void* pos, u32 size)
        CARDi_ReadRom(dmad, pos, dest, size, callb, archPtr, false);
        return 0;

So we take that code and add our own that will modify the data that's being read if needed.
Here's the code.

typedef unsigned int u32;
#include "nsmb.h"

//Stuff needed for CARDi_ReadRom
#define callb 0x0206AAB4
#define dmad (*(u32*)0x02096100)

//Stuff from the ROM header
#define fatoffs (*(u32*)0x027ffe48)
#define fatsize (*(u32*)0x027ffe4C)

int nsub_0206aa68(void* archPtr,  u8* dest, const void* pos, u32 size)
        CARDi_ReadRom(dmad, pos, dest, size, callb, archPtr, false);
        //if pos is inside the FAT, loop over all read bytes and decrypt them before returning.
        if ((u32)(pos) >= fatoffs && (u32)(pos) < (fatoffs + fatsize))
                for(int i = 0; i < size; i++)
                        dest[i] ^= 0x9B;
        return 0;

And add this to nsmb.h:
void CARDi_ReadRom(u32 dma, const void* src, void* dst, u32 len,  u32 callback, void *arg, bool wtf);

Encrypting the FAT

So we gotta now to encrypt the FAT.
We will do a little test program that will read an unencrypted FAT and write it out encrypted. Very simple code.
#include <iostream>
using namespace std;

int main () {
        char c;

        while (cin.good())
                c = cin.get();
                if (cin.good())
                        cout.put(c ^ 0x9B);

Save it to xor.cpp. Then compile it:
g++ xor.cpp -o xor

Then open the ROM in NSMBe, extract fat.bin.
Encrypt the fat using the little program we just compiled:
./xor < fat.bin > fatenc.bin

And then import fatenc.bin into NSMBe.
Now close NSMBe immediately, don't do anything else or it will try to do stuff with the encrypted FAT and crash.
This is the last time you will see your ROM open in NSMBe


This kind of encryption is easy to hack, because it's easy to look at the ROM in an hex editor, look at the FAT, and see that it's been XOR encrypted.

Here's a part of the encrypted FAT:
9F 2D 1E 9B 9F 2D 1E 9B 03 5F 1E 9B 03 5F 1E 9B 43 6A 1E 9B 43 6A 1E 9B BF 6E 1E 9B BF 6E 1E 9B 46 62 1E 9B 7B 62 1E 9B 4B 65 1E 9B 4B 65 1E 9B 27 BD 1D 9B 27 BD 1D 9B B7 C2 1D 9B B7 C2 1D 9B 8F C1 1D 9B 8F C1 1D 9B CF 1E 1D 9B CF 1E 1D 9B 57 20 1D 9B 57 20 1D 9B 53 26 1D 9B 53 26 1D 9B C5 25 1D 9B FB 25 1D 9B 2A 50 1D 9B 2F 50 1D 9B

See all those bytes that are 9B? These were 00 in the original FAT. 0x00 ^ 0x9B = 0x9B.
So it's veeeery easy to see the "encryption key" which is 0x9B.

Pretty much any non-n00b will think about XOR encryption when looking ath this. And making a program to decrypt the FAT is easy, it's just XORing again with 0x9B.

So this encryption will stop most n00bs from stealing your tilesets. But there may be non-n00bs who want to steal your tilesets. (I have no idea why. If they aren't noobs they could make their own as well. But people are stupid, the internet works this way )

So you will probably want something better, There's hundreds of ways of improving this. You could encrypt with a stream of pseudorandom numbers. You could even encrypt MORE stuff in the ROM. Nearly all of it, you have to leave header, binaries and icon/title alone so loaders can load it properly.

So, yeah. That's it.
Posted on 12-13-12, 12:24 pm
Fuzz Ball
KirbyFanatic64 (LOL)

Karma: 1281
Posts: 714/950
Since: 11-13-11
Even though I don't understand it fully (as I'm not into programming), I find this very useful.

I'll try with my old rom.

Thanks for this awesome tutorial!

Great games must be fun, not fancy.

Music Hacker needed! PM me if you wish!
Pages: 1