samedi 24 avril 2010

Packet Tracer analysis

Packet Tracer analysis

Hi,
/My life on
I have a friend who's teaching Network courses at our school. During those courses his students have to do some labs (configure router&switches, to apply the CCNA curriculum) using the rack we have or Packet Tracer (a tool from Cisco to simulate a Cisco network and train yourself, at configuring the devices). I used Packet Tracer some years ago, and the new version has quite a few new functions. One the new function (I don't remember it when I used Packet Tracer, may be it already existed before ^^) is the activity wizard. You can create your graded lab (*.pka file), give it to your students and after a pre-defined time in the lab get it back and Packet Tracer will give you the grade of the student according to the configurations he made on lab's network, nice isn't it?
/My life off

Well I'm gonna show you how to read those *.pka file, which contains the lab, cuz if you can read it you have all the responses =) and the corresponding grade ^^ =D
I'm using the version 5.2.0.0068, so the described method might change in the future version.

If you try to open a *.pka file, you won't be able to read anything in clear. Let's look at how packet Tracer handles those files and see if we can get them clear without knowing the activity wizard password.

(I'm using OllyDbg for the reversing/debugging that follows.)
Launch PT (Packet Tracer) and attach Olly to it. I've spend quite a lot of time reversing PT but we will go straight to the point ^^. After putting some breakpoints on library file management functions, (if we look at the PT's directory we can see that is uses QT4 dlls see http://doc.trolltech.com/4.1/qfile.html for file management under QT4), our pka file is open at (if we open it by the way of the "open - recent files")

CPU Disasm
Address Hex dump Command
0040E3B2 FF15 F4FB8D01 CALL DWORD PTR DS:[<&QtCore4.?open@QFile@@UAE_NV?$QFlags@W4OpenModeFlag@QIODevice@@@@@Z>]

Some bytes further our pka file is red and closed by the readAll and close function (inherited from QIODevice).

CPU Disasm
Address Hex dump Command
0040E3CA FF15 C4FB8D01 CALL DWORD PTR DS:[<&QtCore4.?readAll@QIODevice@@QAE?AVQByteArray@@XZ>]
0040E3D0 C645 FC 03 MOV BYTE PTR SS:[LOCAL.1],3
0040E3D4 8D4D EC LEA ECX,[LOCAL.5]
0040E3D7 FF15 0CFC8D01 CALL DWORD PTR DS:[<&QtCore4.?close@QFile@@UAEXXZ>]

The readAll function returns us a QByteArray with the content of our file.
A pointer on this QByteArray is pushed at 0x0040E3E4 as argument to the function 0x0040E570.

CPU Disasm
Address Hex dump Command
0040E3E4 50 PUSH EAX
0040E3E5 8B4D A8 MOV ECX,DWORD PTR SS:[LOCAL.22]
0040E3E8 8B11 MOV EDX,DWORD PTR DS:[ECX]
0040E3EA 8B4D A8 MOV ECX,DWORD PTR SS:[LOCAL.22]
0040E3ED 8B82 4C010000 MOV EAX,DWORD PTR DS:[EDX+14C]
0040E3F3 FFD0 CALL EAX

This function (at 0x0040E570) is used to clear the file in memory, to be readable by the program. Let's see how this function clears the file.

In this function there is a loop from 0x0040E635 to 0x0040E684.
First, this loop will first get the QByteArray size in memory:

0040E641 FF15 A4FB8D01 CALL DWORD PTR DS:[<&QtCore4.?size@QByteArray@@QBEHXZ>]

(The loop will the QByteArray size times (for each character of the QByteArray)).
Second, on each iteration the program will get the character at the beginning of the QByteArray + offset of the loop.

0040E64F 51 PUSH ECX
0040E653 FF15 ACFB8D01 CALL DWORD PTR DS:[<&QtCore4.?at@QByteArray@@QBE?BDH@Z>

Third, the program makes a XOR between the character extracted and the QByteArray size minus the position of the current character.

0040E65F FF15 A4FB8D01 CALL DWORD PTR DS:[<&QtCore4.?size@QByteArray@@QBEHXZ>]
0040E665 2B45 E4 SUB EAX,DWORD PTR SS:[LOCAL.7]
0040E668 33F0 XOR ESI,EAX

And finally the result is wrote to a new QByteArray.

0040E67E FF15 B4FB8D01 CALL DWORD PTR DS:[<&QtCore4.??4QByteRef@@QAEAAV0@D@Z>]

This new QByteArray is now uncompressed by the function call at 0x0040E696. This function uses qUncompress.

00427773 FF15 A0FB8D01 CALL DWORD PTR DS:[<&QtCore4.?qUncompress@@YA?AVQByteArray@@PBEH@Z>]

And the new QByteArray returned is now the clear XML file of the practice ; ) and u can see all the responses ^^ You can also see the hashed password protecting the lab at the marker
<activity pass="" timertype="" enabled="" countdownms="">
, it's a MD5 without salt.

QT developed a customized IDE for QT development so i coded a little program to get the XML file from a .pka protected file (their IDE is really nice and easy to use ^^).

Hope someone will find this helpful.


#include <qtcore/qcoreapplication>
#include <qfile>
#include <qdir>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QFile fileToDecipher;
QFile decipheredFile;
qint64 cipheredFileSize;
unsigned char stock;
string fileName;
QByteArray inputFile, outputFile;

cout << "Please type the filename to decipher : ";
cin >> fileName;
cout << endl;

fileToDecipher.setFileName(QString::fromStdString(fileName));
decipheredFile.setFileName(QString(QString::fromStdString(fileName)).prepend("deciphered_"));

if (!fileToDecipher.open(QIODevice::ReadOnly))
{
cout << "ERROR : Can't open the specified file." << endl << "Program aborted." ;
cin.ignore();
cin.ignore();
return -1;
}
if(!decipheredFile.open(QIODevice::ReadWrite))
{
cout << "ERROR : Can't create the decphered file." << endl << "Program aborted." ;
cin.ignore();
cin.ignore();
return -1;
}

cipheredFileSize = fileToDecipher.size();
cout << "Size of the Packet Tracer file : " << cipheredFileSize << endl;
cout << "Deciphering the file ..." << endl;

inputFile = fileToDecipher.readAll();

for(int i=0; i < cipheredFileSize;i++)
{
stock = (unsigned char)(cipheredFileSize-i)^inputFile[i];
outputFile[i] = stock;
}

outputFile = qUncompress(outputFile);
decipheredFile.write(outputFile);

decipheredFile.close();
fileToDecipher.close();

printf("Done.");
cin.ignore();
cin.ignore();
return 0;
}


PS: if you just wanna bypass the activity wizard protection just nop the check at 0x00410B7A ;-)