Hacking Hacking with 3DS Save DeEncrypter

Status
Not open for further replies.

Immortal_no1

Well-Known Member
OP
Member
Joined
Jul 17, 2003
Messages
266
Trophies
0
XP
292
Country
It's a complicated process, and one that i'll get around to looking at in a little while once i'm finished with the current checksum part of the app.
 

ichichfly

Well-Known Member
Member
Joined
Sep 23, 2009
Messages
619
Trophies
1
XP
1,076
Country
Gambia, The
This is a little programm that work with the early 3ds games (on newer it will result in unsorted data).

It makes out of the raw decrypted save file the virtual save file

it may maps some sectors that are no more used to its old locations but well. All unused virtual locations are shown as 0xFF blocks.

use it this way 3dssaveresorter.exe

it will create a list than you have to press enter and the out file is created

http://www.mediafire.com/?02hhp317m2bipdh

hope this help
 

Immortal_no1

Well-Known Member
OP
Member
Joined
Jul 17, 2003
Messages
266
Trophies
0
XP
292
Country
That is an interesting app, i will have to play about with it a little bit and see how useful it might be.

With the file correctly mapped it should be possible to extract the files from within, although any replacing of the files will have to reverse the process. Who wrote the application? is there source available?
 

ichichfly

Well-Known Member
Member
Joined
Sep 23, 2009
Messages
619
Trophies
1
XP
1,076
Country
Gambia, The
Immortal_no1 said:
That is an interesting app, i will have to play about with it a little bit and see how useful it might be.

With the file correctly mapped it should be possible to extract the files from within, although any replacing of the files will have to reverse the process. Who wrote the application? is there source available?


I wrote it. The src sorry I have to clean it a bit befor I releas it. There are many commentars that are not good etc. and I have made it togetter with a app to get the offsets for the files and other blocks out of that file I clean it up a bit and releas it.

ADD:Sorry for may bad english

ADD2: the other app http://www.mediafire.com/?vufn0s5q2j9akfc src follow soon

@ADD2: virtual_3ds_save_file_analyser.exe in must be a virtual save

ADD3:But first I update the 3dssaveresorter to work with newer games

ADD4: it should result in something like this

Partition0:
hashtabeloffset 2000
SAVEoffset 3000
SAVEsize D000

Partition1:
hashtabeloffset 10000
SAVEoffset 11000
SAVEsize D000

SAVE0:
savestatoffs 3400
filetabeloffs 3600

rootname: files: 2
system.dat (0) start: 3800 size: 22
save00.bin (1) start: 3a00 size: 14dc

SAVE1:
savestatoffs 11400
filetabeloffs 11600

rootname: files: 2
system.dat (0) start: 11800 size: 22
save00.bin (1) start: 11a00 size: 14dc

so with a hex editor you can now easy extract the files (this is a Zelda save)

ADD5: src of the 3dssaveresorter

CODE static void Main(string[] args)
{

FileStream instream = new FileStream(args[0],FileMode.Open);
FileStream outstream = new FileStream(args[1], FileMode.Create);
UInt32 chucksize = 0x1000;//Convert.ToUInt32(args[3], 16);
UInt32 joffset = (UInt32)((instream.Length / chucksize) * 0xA);//Convert.ToUInt32(args[3], 16);

//we ignore all header_entrys since this is only for beta stuff
Byte[,] sectors = new Byte[0x10000,3]; // virt_sec , phys_sec , virt_realloc_cnt

Byte[,] usedsectors = new Byte[(UInt32)(instream.Length / chucksize), 3];

instream.Seek(joffset,SeekOrigin.Begin);
int size = 0;
for(int i = 0; instream.Position < 0xFFF;i++)
{
sectors[i, 0] = (Byte)instream.ReadByte();
instream.ReadByte();
sectors[i, 1] = (Byte)instream.ReadByte();
instream.Seek(2, SeekOrigin.Current);
sectors[i, 2] = (Byte)instream.ReadByte();
instream.Seek(0x1A, SeekOrigin.Current);
if (sectors[i, 1] != 0xFF) Console.WriteLine(sectors[i, 1].ToString() + " -> " + sectors[i, 0].ToString() + "(" + sectors[i, 2].ToString() + ")");
size++;
}
Console.ReadLine();

for (int i = 0; i < (UInt32)(instream.Length / chucksize); i++)
{
for (int i2 = 0; i2 < size; i2++)
{
if (sectors[i2, 1] != 0xFF) // no 0xFF
{
if (usedsectors[i, 2]
 

Immortal_no1

Well-Known Member
OP
Member
Joined
Jul 17, 2003
Messages
266
Trophies
0
XP
292
Country
Good job on the App, nicely written source, is that in .NET?

Seems like you have a pretty good understanding of the save structure...

I have a nice collection, i'll post a .rar with the saves at some point soon, i'm actually going to have to play the games that i have now rather than leaving them in there nice pristine condition
tongue.gif
 

cojiro

Well-Known Member
Member
Joined
Apr 1, 2009
Messages
152
Trophies
0
Age
33
Location
Boston, MA
Website
Visit site
XP
171
Country
United States
Immortal_no1 said:
Good job on the App, nicely written source, is that in .NET?

Seems like you have a pretty good understanding of the save structure...

I have a nice collection, i'll post a .rar with the saves at some point soon, i'm actually going to have to play the games that i have now rather than leaving them in there nice pristine condition
tongue.gif

looks like C#/.NET to me. Its awesome that both of you are making such progress!
 

Immortal_no1

Well-Known Member
OP
Member
Joined
Jul 17, 2003
Messages
266
Trophies
0
XP
292
Country
ichichfly, by using your application, can you explain to me where the locations for the parts that differ are. so we want to find :

02 16 1C 0A 03 03
12 15 08 0F 03 03
00 14 0B 06 03 03
00 13 19 0B 03 03
00 0F 15 19 03 03

As at these points they have different values. If we know where the location of these parts are we may be able to find out how the values are generated.

48
C0

47
3A 47

Save3-CCCCCCCC-Analysis.pdf
 

ichichfly

Well-Known Member
Member
Joined
Sep 23, 2009
Messages
619
Trophies
1
XP
1,076
Country
Gambia, The
Immortal_no1 said:
ichichfly, by using your application, can you explain to me where the locations for the parts that differ are. so we want to find :

02 16 1C 0A 03 03
12 15 08 0F 03 03
00 14 0B 06 03 03
00 13 19 0B 03 03
00 0F 15 19 03 03

As at these points they have different values. If we know where the location of these parts are we may be able to find out how the values are generated.

48
C0

47
3A 47

Save3-CCCCCCCC-Analysis.pdf



02 16 1C 0A 03 03 virtual: 0x2000 phys: 0x1C000
12 15 08 0F 03 03 virtual: 0x12000 phys: 0x8000
00 14 0B 06 03 03 virtual: 0x0 phys: 0xB000
 

Immortal_no1

Well-Known Member
OP
Member
Joined
Jul 17, 2003
Messages
266
Trophies
0
XP
292
Country
Right, thanks for that, that helps. The locations 1C000 8000 and 15000 helps out, as the data at the address 8000 is where the scores are saved, the data at 1C000 I thinkl was the location of the crc and the location of 15000 I think is the save bloc for the filesystem... might be wrong on that part, I'm doing this from memory as I'm writing on my mobile.

Do you mind if I rewrite your app for integration into the Save De/Encrypter? Hopefully we may be able to get the extraction and importing of the save data working.

I've written an application that will run through the save file and it's searching for the checksums that we don't yet have. The search shoiuld finish tomorrow night so I'll update this post then.

Preliminary searches has found the DIFI / DISA hashes.
 

ichichfly

Well-Known Member
Member
Joined
Sep 23, 2009
Messages
619
Trophies
1
XP
1,076
Country
Gambia, The
Immortal_no1 said:
Right, thanks for that, that helps. The locations 1C000 8000 and 15000 helps out, as the data at the address 8000 is where the scores are saved, the data at 1C000 I thinkl was the location of the crc and the location of 15000 I think is the save bloc for the filesystem... might be wrong on that part, I'm doing this from memory as I'm writing on my mobile.

Do you mind if I rewrite your app for integration into the Save De/Encrypter? Hopefully we may be able to get the extraction and importing of the save data working.

I've written an application that will run through the save file and it's searching for the checksums that we don't yet have. The search shoiuld finish tomorrow night so I'll update this post then.

Preliminary searches has found the DIFI / DISA hashes.

@Do you mind if I rewrite your app for integration into the Save De/Encrypter? Hopefully we may be able to get the extraction and importing of the save data working.

you are allowed to add it (you can also integrat the virtual save game analyser)

ADD: the src of the virtual save game analyser that gets the positions it is still not good but I fixed some things up now more games should work

CODEclass Program
{
static void Main(string[] args)
{
uint[] partoffset = new uint[0x10];
uint[] partsize = new uint[0x10];
uint[] parthashsize = new uint[0x10];
UInt16[] savestatoffs = new UInt16[0x10];
uint[] filetabeltoffs = new uint[0x10];
int i = 0;
FileStream instream = new FileStream(args[0],FileMode.Open);
instream.Seek(0x200, SeekOrigin.Begin);
while (true) //file system search
{
if (instream.ReadByte() == 0x44 && instream.ReadByte() == 0x49 && instream.ReadByte() == 0x46 && instream.ReadByte() == 0x49) //is DIFI
{
if (i == 0)
{
partoffset[0] = 0x2000;
instream.Seek(0x9C - 4, SeekOrigin.Current);
Byte[] buffer = new Byte[4];
instream.Read(buffer, 0, 4);
parthashsize = BitConverter.ToUInt32(buffer, 0);

instream.Seek(4, SeekOrigin.Current);

instream.Read(buffer, 0, 4);

partsize = BitConverter.ToUInt32(buffer, 0);

Console.WriteLine("Partition" + i.ToString() + ":");
Console.WriteLine("hashtabeloffset " + Convert.ToString(partoffset, 16).ToUpper());
Console.WriteLine("SAVEoffset " + Convert.ToString(partoffset + parthashsize, 16).ToUpper());
Console.WriteLine("SAVEsize " + Convert.ToString(partsize, 16).ToUpper());
Console.WriteLine("");
instream.Seek(0x88, SeekOrigin.Current);
}
else
{
partoffset = partoffset[i - 1] + parthashsize[i - 1] + partsize[i - 1];
if (partoffset % 0x1000 != 0) partoffset = (partoffset / 0x1000) * 0x1000 + 0x1000;
instream.Seek(0x9C - 4, SeekOrigin.Current);
Byte[] buffer = new Byte[4];
instream.Read(buffer, 0, 4);
parthashsize = BitConverter.ToUInt32(buffer, 0);

instream.Seek(4, SeekOrigin.Current);

instream.Read(buffer, 0, 4);

partsize = BitConverter.ToUInt32(buffer, 0);

Console.WriteLine("Partition" + i.ToString() + ":");
Console.WriteLine("hashtabeloffset " + Convert.ToString(partoffset, 16).ToUpper());
Console.WriteLine("SAVEoffset " + Convert.ToString(partoffset + parthashsize, 16).ToUpper());
Console.WriteLine("SAVEsize " + Convert.ToString(partsize, 16).ToUpper());
Console.WriteLine("");
}
}
else
{
break;
}
i++;
}
for (int i2 = 0; i != i2; i2++)
{
instream.Seek(partoffset[i2] + parthashsize[i2], SeekOrigin.Begin);
if (instream.ReadByte() == 0x53 && instream.ReadByte() == 0x41 && instream.ReadByte() == 0x56 && instream.ReadByte() == 0x45) //is SAVE
{
Console.WriteLine("SAVE" + i2.ToString() + ":");
instream.Seek(0x58 - 4, SeekOrigin.Current);
Byte[] buffer = new Byte[4];
Byte[] buffer1 = new Byte[2];
instream.Read(buffer1, 0, 2);
savestatoffs[i2] = BitConverter.ToUInt16(buffer1, 0);

instream.Seek(0x12, SeekOrigin.Current);
instream.Read(buffer, 0, 4);
filetabeltoffs[i2] = BitConverter.ToUInt32(buffer, 0) * 0x200;
Console.WriteLine("savestatoffs " + Convert.ToString(savestatoffs[i2] + partoffset[i2] + parthashsize[i2], 16).ToUpper());
Console.WriteLine("filetabeloffs " + Convert.ToString(savestatoffs[i2] + filetabeltoffs[i2] + partoffset[i2] + parthashsize[i2], 16).ToUpper());
Console.WriteLine("");
if ((savestatoffs[i2] + filetabeltoffs[i2]) == 0)
{
Console.WriteLine("calculated filetabel offset wrong");
}
else
{
instream.Seek(savestatoffs[i2] + filetabeltoffs[i2] + partoffset[i2] + parthashsize[i2], SeekOrigin.Begin);
instream.Read(buffer, 0, 4);
uint files = BitConverter.ToUInt32(buffer, 0) - 1;
Byte[] name = new byte[0x10];
instream.Read(name, 0, 0x10);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
Console.WriteLine("rootname:" + enc.GetString(name) + " files: " + files.ToString());
instream.Seek(0x20 - 4, SeekOrigin.Current);
for (int currfile = 0; currfile < files; currfile++)
{
instream.Read(buffer, 0, 4);
uint currentfiles = BitConverter.ToUInt32(buffer, 0) - 1;
instream.Read(name, 0, 0x10);
instream.Read(buffer, 0, 4);
uint index = BitConverter.ToUInt32(buffer, 0);
instream.Seek(4, SeekOrigin.Current);
instream.Read(buffer, 0, 4);
uint offset = BitConverter.ToUInt32(buffer, 0);
instream.Read(buffer, 0, 4);
uint curfilesize = BitConverter.ToUInt32(buffer, 0);
if (currentfiles == 0) Console.WriteLine(enc.GetString(name) + "(" + index.ToString() + ")" + " start: " + Convert.ToString(offset * 0x200 + savestatoffs[i2] + partoffset[i2] + parthashsize[i2], 16) + " size: " + Convert.ToString(curfilesize, 16));
else Console.WriteLine(" " + enc.GetString(name) + " (next " + currentfiles.ToString() + " files)");
instream.Seek(0xC, SeekOrigin.Current);
}
}
Console.WriteLine("");
}
else
{
Console.WriteLine("SAVE" + i2.ToString() + " is not there");
}
}
Console.ReadLine();
}
public static UInt16 Swap(UInt16 input)
{
return ((UInt16)(
((0xFF00 & input) >> 8) |
((0x00FF & input) > 24) |
((0x00FF0000 & input) >> 8) |
((0x0000FF00 & input)
 

Immortal_no1

Well-Known Member
OP
Member
Joined
Jul 17, 2003
Messages
266
Trophies
0
XP
292
Country
I also just got Rayman 3D and Asphault 3D too, I'll upload these later.

That's quite a change to the application
 

Immortal_no1

Well-Known Member
OP
Member
Joined
Jul 17, 2003
Messages
266
Trophies
0
XP
292
Country
Anyone have any ideas on how the checksums for the header data is created?

looking at the numbers at the bottom of the page in the file here


My checksum search application is going to take a long time to go through all the possible permutations
as i think there's only a few thousand trillion. I'm going to leave it running and see what we find.
 

ichichfly

Well-Known Member
Member
Joined
Sep 23, 2009
Messages
619
Trophies
1
XP
1,076
Country
Gambia, The
Immortal_no1 said:
Anyone have any ideas on how the checksums for the header data is created?

looking at the numbers at the bottom of the page in the file here


My checksum search application is going to take a long time to go through all the possible permutations
as i think there's only a few thousand trillion. I'm going to leave it running and see what we find.

the checksum may be calculated over the encrypted data.
 

Immortal_no1

Well-Known Member
OP
Member
Joined
Jul 17, 2003
Messages
266
Trophies
0
XP
292
Country
I wonder if the hex values are added together and then a modulus is used. I'll write a test app to see if that works. Otherwise I'm starting to run out of ideas...
 

CollosalPokemon

ばん。。。かい
Member
Joined
Oct 18, 2009
Messages
682
Trophies
0
XP
1,724
Country
United States
Immortal_no1 said:
I wonder if the hex values are added together and then a modulus is used. I'll write a test app to see if that works. Otherwise I'm starting to run out of ideas...

I'm not sure if this'll help or not but I don't think we need to modify any blocks (ie 0xB000 -> 0xBFFF, 0x4000 -> 0x4FFF, or w/e) found in the current 3DS Save DeEncryptor because I tested it by copying just the blocks and checksums found with the experimental menu from one save to a different one (same game) and it still ended up corrupted.

Basically whatever needs to be done now to finish modifying the saves is simply not in the blocks found by the experimental menu (ie 0xB000 -> 0xBFFF, 0x4000 -> 0x4FFF, or w/e), or at least, that's the case for Legend of Zelda: Ocarina Of Time 3D. I think it has something to do with the data inbetween the blocks found in the experimental menu.
 

Immortal_no1

Well-Known Member
OP
Member
Joined
Jul 17, 2003
Messages
266
Trophies
0
XP
292
Country
CollosalPokemon said:
Immortal_no1 said:
I wonder if the hex values are added together and then a modulus is used. I'll write a test app to see if that works. Otherwise I'm starting to run out of ideas...

I'm not sure if this'll help or not but I don't think we need to modify any blocks (ie 0xB000 -> 0xBFFF, 0x4000 -> 0x4FFF, or w/e) found in the current 3DS Save DeEncryptor because I tested it by copying just the blocks and checksums found with the experimental menu from one save to a different one (same game) and it still ended up corrupted.

Basically whatever needs to be done now to finish modifying the saves is simply not in the blocks found by the experimental menu (ie 0xB000 -> 0xBFFF, 0x4000 -> 0x4FFF, or w/e), or at least, that's the case for Legend of Zelda: Ocarina Of Time 3D. I think it has something to do with the data inbetween the blocks found in the experimental menu.

Did you change the header data too? if you compare the super monkeyball saves there are a few differences between them, including the header data there are also still 3 checksums that we don't know how their generated and one of those isn't an sha-256.

Now whether all those checksums need to be changed isn't known. Chances are they do however they may just be there to checksum the backup data and they're only used if your main game save gets messed up. In which case as soon you save the game after playing it, it will replace those checksums. I think the most important area at the moment is the header. As that would be the first thing to be checked.
 
Status
Not open for further replies.

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
    NinStar @ NinStar: I'm not doing ok, everywhere I go I see sex