nForce
1st July 2006, 16:20
I decided to reverse engineer the encryption algorithm responsible for encrypting npgm.erl.
It was a little bit more difficult but you will see the algorithm is still very similar to what was used for npgg.erl and npgl.erl. Unlike the previous encryption system, a strange timestamp is stored where the remainder of a specific operations is XORd with 85 (55h). This remainder is the index to a key table which I have extracted. By obtaining the remainder once again we can extract the correct key from the key table and feed it into the algorithm we are familiar with, the same one used for decrypting npgg/npgl. Proof of concept code is provided bellow.
Please remember, the key table may differ depending on the game as well there are some other values that might change depending on the game.
/************************************************** **********************************
* Project: ERL (Type2) Decryptor *
* *
* Copyright (C) 2006 by nForce *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
************************************************** **********************************/
#include <iostream>
#include <fstream>
using namespace std;
int main ( int nArguments, const char * szArguments [] )
{
static const unsigned long nKeyTable [] =
{
0x14C4D0D4, 0x2F4F636C,
0x1BCBD3D8, 0x3888B0B8,
0xB83888B0, 0x70314171,
0x080B0B03, 0x29C9E1E8,
0x0E8E828C, 0x34340430
};
if ( nArguments > 1 )
{
std::string sOutput ( szArguments [ 1 ] );
ifstream ifsTargetFile ( sOutput.c_str (), ios::binary );
if ( ifsTargetFile.is_open () )
{
sOutput.append ( ".dec" );
ofstream ofsOutputFile ( ( sOutput.c_str () ) );
if ( ofsOutputFile.is_open () )
{
unsigned long nTimeStamp;
ifsTargetFile.read ( ( ( char * ) ( & nTimeStamp ) ), 4 );
unsigned long nKeyIndex = ( ( ( nTimeStamp >> 16 ) & 0xff ) ^ 0x55 );
if ( nKeyIndex >= 10 )
{
cout << "Error: Key index is out of range!" << endl;
return 1;
}
unsigned long nCryptoKey = nKeyTable [ nKeyIndex ];
while ( ifsTargetFile.good () )
{
nCryptoKey = ( nCryptoKey * 3 + 1 );
unsigned char nEncryptedByte;
ifsTargetFile.get ( ( * ( char * ) ( & nEncryptedByte ) ) );
unsigned char nCryptoByte = ( unsigned char ) ( nCryptoKey & 0xff );
nCryptoByte = nCryptoByte + 0x58;
nEncryptedByte = nCryptoByte ^ nEncryptedByte;
ofsOutputFile.put ( ( * ( char * ) ( & nEncryptedByte ) ) );
}
ofsOutputFile.close ();
}
else
{
cout << "Unable to open " << sOutput.c_str () << " for output!" << endl;
}
ifsTargetFile.close ();
}
else
{
cout << "Unable to open " << sOutput.c_str () << " for input!" << endl;
}
}
else
{
cout << "Syntax Error!" << endl
<< "Syntax: " << szArguments [ 0 ] << " <Erl File>" << endl;
}
return 0;
}
It was a little bit more difficult but you will see the algorithm is still very similar to what was used for npgg.erl and npgl.erl. Unlike the previous encryption system, a strange timestamp is stored where the remainder of a specific operations is XORd with 85 (55h). This remainder is the index to a key table which I have extracted. By obtaining the remainder once again we can extract the correct key from the key table and feed it into the algorithm we are familiar with, the same one used for decrypting npgg/npgl. Proof of concept code is provided bellow.
Please remember, the key table may differ depending on the game as well there are some other values that might change depending on the game.
/************************************************** **********************************
* Project: ERL (Type2) Decryptor *
* *
* Copyright (C) 2006 by nForce *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
************************************************** **********************************/
#include <iostream>
#include <fstream>
using namespace std;
int main ( int nArguments, const char * szArguments [] )
{
static const unsigned long nKeyTable [] =
{
0x14C4D0D4, 0x2F4F636C,
0x1BCBD3D8, 0x3888B0B8,
0xB83888B0, 0x70314171,
0x080B0B03, 0x29C9E1E8,
0x0E8E828C, 0x34340430
};
if ( nArguments > 1 )
{
std::string sOutput ( szArguments [ 1 ] );
ifstream ifsTargetFile ( sOutput.c_str (), ios::binary );
if ( ifsTargetFile.is_open () )
{
sOutput.append ( ".dec" );
ofstream ofsOutputFile ( ( sOutput.c_str () ) );
if ( ofsOutputFile.is_open () )
{
unsigned long nTimeStamp;
ifsTargetFile.read ( ( ( char * ) ( & nTimeStamp ) ), 4 );
unsigned long nKeyIndex = ( ( ( nTimeStamp >> 16 ) & 0xff ) ^ 0x55 );
if ( nKeyIndex >= 10 )
{
cout << "Error: Key index is out of range!" << endl;
return 1;
}
unsigned long nCryptoKey = nKeyTable [ nKeyIndex ];
while ( ifsTargetFile.good () )
{
nCryptoKey = ( nCryptoKey * 3 + 1 );
unsigned char nEncryptedByte;
ifsTargetFile.get ( ( * ( char * ) ( & nEncryptedByte ) ) );
unsigned char nCryptoByte = ( unsigned char ) ( nCryptoKey & 0xff );
nCryptoByte = nCryptoByte + 0x58;
nEncryptedByte = nCryptoByte ^ nEncryptedByte;
ofsOutputFile.put ( ( * ( char * ) ( & nEncryptedByte ) ) );
}
ofsOutputFile.close ();
}
else
{
cout << "Unable to open " << sOutput.c_str () << " for output!" << endl;
}
ifsTargetFile.close ();
}
else
{
cout << "Unable to open " << sOutput.c_str () << " for input!" << endl;
}
}
else
{
cout << "Syntax Error!" << endl
<< "Syntax: " << szArguments [ 0 ] << " <Erl File>" << endl;
}
return 0;
}