Jump to content

help with source code to decrypt .sav from bw


marcos9191

Recommended Posts

I want to make a "team checker" fifth generation physical tournament but there is a part that does not work well. The part of the block itself, but to decrypt the .sav file doesn't work.

First I want to try a pkm extracted directly from the .sav file and then adapt it to the team.

I followed this guide but for some reason I'm god at the result, for example bit 8 = c8 (encrypted) would have to get out 83 (decryption) but does not give me that number.

Link of source code and reshiram (normal pkm and encrypted extracted from bw .sav):

Anyone know what is wrong? I want to learn how it works

-----------

public class PokemonTeamCheck5Gen {

public static void main(String[] args)throws Exception{

//block a-b-c-d (0-24)

Long pid = 3621399865L;

int a = Math.round(((pid & 0x3E000) >> 0xD) % 24);

System.out.println(a);

//Read encrypted pkm extracted from .sav (0x18E08 - 0x18E0B = pid poke 1)

JFileChooser fileopen = new JFileChooser();

fileopen.setDialogTitle("Team Checker (Written by marcos91)");

if (fileopen.showOpenDialog(null) == JFileChooser.APPROVE_OPTION){

File file = fileopen.getSelectedFile();

FileInputStream fis = new FileInputStream(file);

DataInputStream input = new DataInputStream(fis);

int[] pkm = new int[136];

for (int i=0; i<136; i++){

pkm = input.read();

}

//checksum = 0x27dd (dd27 in little endian)

//Decrypt data

//x[i+1] = x ^ Math.round((0x41C64E6D * 0xdd27 + 0x6073));

int[] x = new int[136];

for (int j=8; j<136; j++){

x[j] = x[j] ^ Math.round((0x41C64E6D * 0x27dd + 0x6073));

System.out.println(x[j]);

}

}

}

}

TeamChecker..zip

TeamChecker..zip

Link to comment
Share on other sites

Ok, this is gonna be difficult for me because Java doesn't support unsigned numbers, so I hope this helps

First this piece of your code is the rand() function that will give you the next seed, however seems you are using the same seed for all bytes

//Decrypt data

for (int j=8; j<136; j++){

x[j] = x[j] ^ Math.round((0x41C64E6D * 0x27dd + 0x6073));

System.out.println(x[j]);

}

Instead use a method and a static variable

static long seed;
public static long rand(){
	seed = (0x41C64E6D * seed + 0x6073) & 0xFFFFFFFF; //This is to make sure it is an unsigned integer
	return seed>>>16;
}

Before you assign decrypt the bytes, you must now which order are the blocks shuffled, and the XOR is applied on an Unsigned Short, not a byte

//block a-b-c-d (0-24)

Long pid = 3621399865L;

int a = Math.round(((pid & 0x3E000) >> 0xD) % 24);

System.out.println(a);

//Decrypt data

for (int j=8; j<136; j++){

x[j] = x[j] ^ Math.round((0x41C64E6D * 0x27dd + 0x6073));

System.out.println(x[j]);

}

First determine the block order using the pid

int a = Math.round(((pid & 0x3E000) >> 0xD) % 24);
String order = "";
switch (a){ //Determine block order
	case 0: 
		order = "ABCD";
		break;
	case 1: 
		order = "ABDC";
		break;
	case 2: 
		order = "ACBD";
		break;
	case 3: 
		order = "ACDB";
		break;
	case 4: 
		order = "ADBC";
		break;
	case 5: 
		order = "ADCB";
		break;
	case 6: 
		order = "BACD";
		break;
	case 7: 
		order = "BADC";
		break;
	case 8: 
		order = "BCAD";
		break;
	case 9: 
		order = "BCDA";
		break;
	case 10: 
		order = "BDAC";
		break;
	case 11: 
		order = "BDCA";
		break;
	case 12: 
		order = "CABD";
		break;
	case 13: 
		order = "CADB";
		break;
	case 14: 
		order = "CBAD";
		break;
	case 15: 
		order = "CBDA";
		break;
	case 16: 
		order = "CDAB";
		break;
	case 17: 
		order = "CDBA";
		break;
	case 18: 
		order = "DABC";
		break;
	case 19: 
		order = "DACB";
		break;
	case 20: 
		order = "DBAC";
		break;
	case 21: 
		order = "DBCA";
		break;
	case 22: 
		order = "DCAB";
		break;
	case 23: 
		order = "DCBA";
		break;
	}

Then initialize the seed with the checksum, pkm+(pkm[b+1]<<8) will give you the short for the XOR

			int checksum = 0x27dd; //Beginning seed is the checksum
		seed = checksum;
		byte[] x = new byte[136];
		int b = 8;
		long temp=0;
		for (b=8;b<136;){
			switch (order.substring(0, 1)){ //Depending on the order unshuffle bytes
			case "A":
				for (int j=8; j<0x28; j+=2){
					temp = (long) (((pkm[b]+(pkm[b+1]<<8)))^(rand()));
					x[j+1] = (byte) ((byte)((temp & 0xFF00)>>>8));
					x[j] = (byte) ((byte) temp & 0xFF);
					b+=2;
				}
				order = order.replace("A","");
				break;
			case "B":
				for (int j=0x28; j<0x48; j+=2){
					temp = (long) (((pkm[b]+(pkm[b+1]<<8)))^(rand()));
					x[j+1] = (byte) ((byte)((temp & 0xFF00)>>>8));
					x[j] = (byte) ((byte) temp & 0xFF);
					b+=2;
				}
				order = order.replace("B","");
				break;
			case "C":
				for (int j=0x48; j<0x68; j+=2){
					temp = (long) (((pkm[b]+(pkm[b+1]<<8)))^(rand()));
					x[j+1] = (byte) ((byte)((temp & 0xFF00)>>>8));
					x[j] = (byte) ((byte) temp & 0xFF);
					b+=2;
				}
				order = order.replace("C","");
				break;
			case "D":
				for (int j=0x68; j<0x88; j+=2){
					temp = (long) (((pkm[b]+(pkm[b+1]<<8)))^(rand()));
					x[j+1] = (byte) ((byte)((temp & 0xFF00)>>>8));
					x[j] = (byte) ((byte) temp & 0xFF);
					b+=2;
				}
				order = order.replace("D","");
				break;
			}

		}

Then just write x[] from 0 to 136, the bytes will be in the correct order

Link to comment
Share on other sites

CODE;

import java.io.*;
import javax.swing.JFileChooser;

//TeamCheck5Gen

public class PokemonTeamCheck5Gen {

//variables estaticas necesarias
static long seed;
public static void main(String[] args)throws Exception{

	//Read encrypted pkm extracted from .sav (0x18E08 - 0x18E0B = pid poke 1)
	JFileChooser fileopen = new JFileChooser();
	fileopen.setDialogTitle("Team Checker (Written by marcos91)");
	if (fileopen.showOpenDialog(null) == JFileChooser.APPROVE_OPTION){
		File file = fileopen.getSelectedFile();
       	FileInputStream fis = new FileInputStream(file);
       	DataInputStream input = new DataInputStream(fis);

		int[] pkm = new int[136];
		for (int i=0; i<136; i++){
			pkm[i] = input.readUnsignedByte();
		}

		//PID
		String b0 = AddZeros(Integer.toHexString(pkm[0x00]));
		String b1 = AddZeros(Integer.toHexString(pkm[0x01]));
		String b2 = AddZeros(Integer.toHexString(pkm[0x02]));
		String b3 = AddZeros(Integer.toHexString(pkm[0x03]));
		String spid = b3+b2+b1+b0;
		long pid = Long.parseLong(spid,16);
		System.out.println(pid);

		//Checksum
		String b6 = AddZeros(Integer.toHexString(pkm[0x06]));
		String b7 = AddZeros(Integer.toHexString(pkm[0x07]));
		int checksum = Integer.parseInt(b7+b6,16);
		System.out.println(checksum);

		//Ordenar bloques ABCD
		int num = Math.round(((pid & 0x3E000) >> 0xD) % 24);
		String orden = "";
		orden = OrdenABCD(num, orden);
		String ordenCopiar = orden;
		System.out.println(num + "=" + orden + "Inverso="+ ordenCopiar);

		//Desencriptar pkm
		seed = checksum; //inicializa la seed con el checksum
		long temp = 0;
		int b = 8;
		int[] x = new int[136];

		for (b=8; b<136;){
			char opc = orden.charAt(0);
			switch(opc){
				case 'A':
					for (int j=8; j<0x28; j+=2){
[b]							temp = (long)(((pkm[b]+(pkm[b+1]<<8)))^(rand()));
						x[j+1] = (byte)((byte)((temp & 0xFF00)>>>8));
						x[j] = (byte)((byte) temp & 0xFF);
						b+=2;[/b]						}
					System.out.println("a entrado en a");
					orden = orden.replace("A","");
					break;

				case 'B':
					for (int j=0x28; j<0x48; j+=2){
						temp = (long)(((pkm[b]+(pkm[b][b+1]<<8)))^(rand()));
						x[j+1] = (byte)((byte)((temp & 0xFF00)>>>8));
						x[j] = (byte)((byte) temp & 0xFF);
						b+=2;[/b]						}
					System.out.println("a entrado en b");
					orden = orden.replace("B","");
					break;

				case 'C':
					for (int j=0x48; j<0x68; j+=2){
[b]							temp = (long)(((pkm[b]+(pkm[b+1]<<8)))^(rand()));
						x[j+1] = (byte)((byte)((temp & 0xFF00)>>>8));
						x[j] = (byte)((byte) temp & 0xFF);
						b+=2;[/b]						}
					System.out.println("a entrado en c");
					orden = orden.replace("C","");
					break;

				case 'D':
					for (int j=0x68; j<0x88; j+=2){
						temp = (long)(((pkm[b]+(pkm[b][b+1]<<8)))^(rand()));
						x[j+1] = (byte)((byte)((temp & 0xFF00)>>>8));
						x[j] = (byte)((byte) temp & 0xFF);
						b+=2;[/b]						}
					System.out.println("a entrado en d");
					orden = orden.replace("D","");
					break;
			}

		}
		//exportar
		String nombre = file.getAbsolutePath()+"exported";
		FileOutputStream fos = new FileOutputStream(nombre);
       	DataOutputStream modificar = new DataOutputStream(fos);

       	//copiar primeros 8 bits (pid, 2 bits sin uso y checksum)
		for(int c=0; c<8; c++){
			x[c] = pkm[c];
			modificar.write(x[c]);
		}

		for(b=8; b<136;){
			char opc2 = ordenCopiar.charAt(0);
			switch(opc2){
				case 'A':
					for (int j=8; j<0x28; j++){
						modificar.write(x[j]);
						b++;
					}
					ordenCopiar = ordenCopiar.replace("A","");
					break;

				case 'B':
					for (int j=0x28; j<0x48; j++){
						modificar.write(x[j]);
						b++;
					}
					ordenCopiar = ordenCopiar.replace("B","");
					break;

				case 'C':
					for (int j=0x48; j<0x68; j++){
						modificar.write(x[j]);
						b++;
					}
					ordenCopiar = ordenCopiar.replace("C","");
					break;

				case 'D':
					for (int j=0x68; j<0x88; j++){
						modificar.write(x[j]);
						b++;
					}
					ordenCopiar = ordenCopiar.replace("D","");
					break;
			}				
		}
	}
}

public static String AddZeros(String b){
	if(b.length() == 1){
		b = "0" + b;
	}
	return b;
}

//Metodo Rand
public static Long rand(){
	seed = (0x41C64E6D * seed + 0x6073) & 0xFFFFFFFF;
	return seed = seed>>>16;
}

public static String OrdenABCD(int num, String orden){
	switch(num){
		case 0: 
			orden = "ABCD";
			break;

		case 1: 
			orden = "ABDC";
			break;

		case 2: 
			orden = "ACBD";
			break;

		case 3: 
			orden = "ACDB";
			break;

		case 4: 
			orden = "ADBC";
			break;

		case 5: 
			orden = "ADCB";
			break;

		case 6: 
			orden = "BACD";
			break;

		case 7: 
			orden = "BADC";
			break;

		case 8: 
			orden = "BCAD";
			break;

		case 9: 
			orden = "BCDA";
			break;

		case 10: 
			orden = "BDAC";
			break;

		case 11: 
			orden = "BDCA";
			break;

		case 12: 
			orden = "CABD";
			break;

		case 13: 
			orden = "CADB";
			break;

		case 14: 
			orden = "CBAD";
			break;

		case 15: 
			orden = "CBDA";
			break;

		case 16: 
			orden = "CDAB";
			break;

		case 17: 
			orden = "CDBA";
			break;

		case 18: 
			orden = "DABC";
			break;

		case 19: 
			orden = "DACB";
			break;

		case 20: 
			orden = "DBAC";
			break;

		case 21: 
			orden = "DBCA";
			break;

		case 22: 
			orden = "DCAB";
			break;

		case 23: 
			orden = "DCBA";
			break;
	}
	return orden;
}
}

Part of getting the pid, the checksum and inicial seed works, but something is not working (I think that is the part in bold)

And also tried to run the program on the first generated file and generates another as the original

Edited by marcos9191
Link to comment
Share on other sites

That's odd. Pretty sure that was a problem with sharing permissions. Also I can't attach .cpp files here (forum won't let me), but the latest ones will always be on my Drive account anyway, so try getting them directly from these links:

pkmds_g5

pokeprng

EDIT: For whatever reason, pokeprng.cpp has no preview, although pkmds_g5 does. So here's a Pastebin, since the PRNG isn't changing anytime soon anwyay: http://pastebin.com/ZwSFnEdu

Additionally, if you have a Google account, I can just add you to the share list so you can sync these files to your computer.

Link to comment
Share on other sites

I've solved:

Method Rand:

seed = (0x41C64E6D * seed + 0x6073) & 0xFFFFFFFF;

return seed;

and line to sequential to shuffle 0x08-0x87:

temp = (long) (((pkm+(pkm[b+1]<<8)))^(rand())>>>16);

Thank's to RubenPikachu and codemonkey85 for help me. Now only remains write check of team gradually and adapt it to the .sav file, in 3-4 weeks I hope to have a version with the basics checks.

Link to comment
Share on other sites

That's odd. Pretty sure that was a problem with sharing permissions. Also I can't attach .cpp files here (forum won't let me), but the latest ones will always be on my Drive account anyway, so try getting them directly from these links:

pkmds_g5

pokeprng

EDIT: For whatever reason, pokeprng.cpp has no preview, although pkmds_g5 does. So here's a Pastebin, since the PRNG isn't changing anytime soon anwyay: http://pastebin.com/ZwSFnEdu

Additionally, if you have a Google account, I can just add you to the share list so you can sync these files to your computer.

I am still facing issues, can you write a .cpp code(command line tool) that will decrypt/encrypt the all PC PKM in the sav file + save it.

The program takes the command like this:

1 for encrypt, 0 for decrypt

./file sav.sav 1   (linux)
or
file.exe sav.sav 1   (windows)

What I have (but this only encrypt individual pkm files)

http://pastebin.com/TvZU6daz

Uses the command
file.exe input.pkm output.pkm

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...