As I said previously, you can only find a 4-byte sequence the way I've described so far. If you want a longer string (for instance, if the 4-byte sequence contains 'non-printable' characters), you have no choice but to cycle through all possible character combinations and test each one's CRC value for a match.
(Although right now I'm thinking of a possible way of finding not 4 but 8 bytes. Or 12, or 16... more on that later.)
Performing a search on the Internet, I came across this little program:
http://w-nz.com/projects/reversecrc/
http://files.seriouszone.com/download.p ... -win32.zip
which originated from here:
http://blog.w-nz.com/archives/2005/07/15/reversing-crc/
http://blog.w-nz.com/archives/2005/07/3 ... rsing-crc/
What it does is brute-force a given number of characters, and calculate a further 4 ones in the way already described. It was used for a very similar purpose, finding matching strings for CRC values from some game (forgot which right now)... but it didn't allow you to indicate a leading or trailing string, so you have to cycle through the characters for the entire string even if you know some of them. Beyond 6 random characters, it starts to take too long for practical purposes.
So, I decided to go ahead and integrate a looping function into my program. I designed a recursive function which would go through all the 'random' characters, increasing the last one progressively until it gets back to the first character in the valid character set (for Trespasser, that character set apparently comprises the lowercase letters a-z, the numbers 0-9, and a few characters more, for a total of 41), then increasing the second-to-last character once and cycling through all possibles for the last one again, increasing the third-to-last once the second has looped through all possible characters, etc. (BTW, that other program does it in the opposite order, which makes the results a bit confusing... my way allows to have them sorted alphabetically, which provides better readability IMO, and also allowe a further improvement which I'll mention later). The function looks like this, after some further additions I'll explain later:
Code:
void wxCRCrevRecFrm::perm(unsigned int z) {
unsigned long crc;
unsigned long b;
wxString anyTextString;
for (b=ValidChars.Find(TestString.GetChar(z)); b<numChars; b++) {
TestString.SetChar(z,ValidChars.GetChar(b));
CRC_buf[z+1]=CRC32((z==LChars-1)?TestString.GetChar(z)+MidString:TestString.GetChar(z),CRC_buf[z]);
if (z<RNDLength-1) {
perm(z+1);
} else {
crc = crc1 ^ CRC_buf[z+1];
if ((ValidChars.Find(crc&0xff)>= 0) & (ValidChars.Find((crc&0xff00)>>8)>= 0) &(ValidChars.Find((crc&0xff0000)>>16)>= 0) &(ValidChars.Find((crc&0xff000000)>>24)>= 0)){
anyTextString=TestString.Left(LChars)+MidString+TestString.Right(RChars);
fputs(anyTextString,fp);
fputc(crc&0xff,fp);
fputc((crc&0xff00)>>8,fp);
fputc((crc&0xff0000)>>16,fp);
fputc((crc&0xff000000)>>24,fp);
fputs("\n",fp);
}
}
if (TestString==EndString) return;
}
TestString.SetChar(z,ValidChars.GetChar(0));
return;
}
I also decided to alternatively implement a non-recursive loop that would do the same as the recursive function. I thought I could use a single counter and modulo arithmetics to change the characters of the 'random' string based on the number of valid characters, but the numbers involved would have been to big for the datatypes available in C++. SO, in the end, I used a "while" loop structure very similar to the one used by the reversecrc program (but again, in the opposite direction):
Code:
//
// start the non-recursive loop to go through the random characters
//
while (true) {
z=RNDLength-1;
crc = crc1 ^ CRC_buf[z+1];
if ((ValidChars.Find(crc&0xff)>= 0) & (ValidChars.Find((crc&0xff00)>>8)>= 0) &(ValidChars.Find((crc&0xff0000)>>16)>= 0) &(ValidChars.Find((crc&0xff000000)>>24)>= 0)){
anyTextString=TestString.Left(LChars)+MidString+TestString.Right(RChars);
fputs(anyTextString,fp);
fputc(crc&0xff,fp);
fputc((crc&0xff00)>>8,fp);
fputc((crc&0xff0000)>>16,fp);
fputc((crc&0xff000000)>>24,fp);
fputs("\n",fp);
}
if (TestString==EndString) break;
do {
b=ValidChars.Find(TestString.GetChar(z));
b++;
b = b % numChars;
TestString.SetChar(z,ValidChars.GetChar(b));
for (i=z; i<RNDLength; i++) {
CRC_buf[i+1]=CRC32((i==LChars-1)?(TestString.GetChar(i)+MidString):TestString.GetChar(i),CRC_buf[i]);
}
z--;
} while ((b==0) && (z>=0) );
}
And the function that allows you to change the character set you ant to loop though is this one:
Code:
/*
* CharSetUpdateUI
*/
void wxCRCrevRecFrm::CharSetUpdateUI(wxUpdateUIEvent& event)
{
if (ValidChars!=CharSet->GetValue()) {
ValidChars=CharSet->GetValue();
numChars=ValidChars.Length();
reCalcStrings();
reCalcCRCs();
}
}
I also added a few buttons that would allow you to easily select some predetermined charcter sets, but those are unimportant. Here are the corresponding functions, anyway:
Code:
/*
* fullAlphaNumCharSetClick
*/
void wxCRCrevRecFrm::fullAlphaNumCharSetClick(wxCommandEvent& event)
{
CharSet->ChangeValue("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
}
/*
* AlphaNumCharSetClick
*/
void wxCRCrevRecFrm::AlphaNumCharSetClick(wxCommandEvent& event)
{
CharSet->ChangeValue("0123456789abcdefghijklmnopqrstuvwxyz");
}
/*
* AlphaCharSetClick
*/
void wxCRCrevRecFrm::AlphaCharSetClick(wxCommandEvent& event)
{
CharSet->ChangeValue("abcdefghijklmnopqrstuvwxyz");
}
So, now I had what I wanted, a program that would allow me to indicate a leading string, a trailing string, and a number of 'random' characters to be brute-forced (it has no point that said number were 0, as then we'd just have the previous case where we only calculated the 4-byte sequence). But then I realized it could be further improved, if we knew a part in the middle of the string we were looking for. Particularly, I had come across a number of entries which had a common string before the 4 final characters. So, I added the possibility to indicate a "middle string", a known string that would go between the 'random' characters and the calculated 4-byte sequence. Now you see how it's so useful to be able to concatenate CRC-32 calculations? But also, there was another series of entries which had a common string before the 6 final characters, not 4. So, I made a further addition, where you could indicate how many of the 'random' characters go AFTER the "middle string". So, if you have indicated X random characters and theen indicate that R should go after (or to the right) of the "middle string", you'd have L=X-R before (or to the left) of it. That's what this line is about:
Code:
anyTextString=TestString.Left(LChars)+MidString+TestString.Right(RChars);
The functions which read both the middle string, total number of 'random' characters and number off characters to the 'right' of the middle string are these:
Code:
/*
* MidString2UpdateUI
*/
void wxCRCrevRecFrm::MidString2UpdateUI(wxUpdateUIEvent& event)
{
MidString=MidString2->GetValue();
reCalcCRCs();
}
/*
* RNDcharsUpdateUI
*/
void wxCRCrevRecFrm::RNDcharsUpdateUI(wxUpdateUIEvent& event)
{
RNDLength=wxAtoi(RNDchars->GetValue());
RChars=wxAtoi(RightChars->GetValue());
LChars=RNDLength-RChars;
if (TestString.Length()!=RNDLength || EndString.Length()!=RNDLength) {
reCalcStrings();
reCalcCRCs();
}
}
You surely noticed tha call to the reCalcStrings() function. That one initializes the 'test' string (the one that's going to havee its characters looped through all possible combinations) and the 'end' string, against which the 'test' string is compared to see if the loop is finished (the recursive version didn't really need the 'end' string originally, but it was very convenient to have it anyway as you'll see when i explain some further changes I made later). So, this is it:
Code:
void wxCRCrevRecFrm::reCalcStrings(void) {
unsigned int i;
//
// prepare everything to start going through the random characters
//
TestString="";
EndString="";
for (i=0; i<RNDLength; i++) {
TestString+=ValidChars.GetChar(0);
EndString+=ValidChars.GetChar(numChars-1);
}
if (StartString->GetValue()!=TestString) StartString->SetValue(TestString);
if (FinalString->GetValue()!=EndString) FinalString->SetValue(EndString);
}
I'll explain those two "if" sentences later.
You've also noticed the call to the reCalcCRC() function. That's another idea I had to further optimize the speed of the calculations. As I already mentioned, it's possible to concatenate CRC calculations. And since we are looping the last character while the previous remain unchanged... why not precalculate the initial values for each position of the 'random' string and store them in a buffer instead of calculating the CRC for the whole string each time a character is changed? That way, when a character is changed, we only need to recalculate the initial values for the following charcaters in the 'random' string. Here is the function:
Code:
void wxCRCrevRecFrm::reCalcCRCs(void) {
unsigned int i;
//
// prepare everything to start going through the random characters
//
for (i=0; i<RNDLength; i++) {
CRC_buf[i+1]=CRC32((i==LChars-1)?(TestString.GetChar(i)+MidString):TestString.GetChar(i),CRC_buf[i]);
}
}
You'll notice it updates the values starting at index 1, not 0. The value for index 0 was already calculated when we got the CRC value from the trailing string. Remember this line:
Code:
CRC_buf[0]=crc0;
Now you see why it's there...
Finally we come to those to "if" lines from a while earlier. The final addition to the program was to add the possibility to manually indicate a starting and ending string for the loop, so that it wouldn't go necessarily from, for example, "aaa" to "zzz" (for 3 random characters and a character set from 'a' to 'z'), but, say, "bud" to "you" (or whatever you want). This is useful if you already know the string you're looking for lies alphabetically between another to strings, or if you don't want to leave the computer on al the while and prefer to do the loop in steps. Here are the functions that read those values:
Code:
/*
* StartStringUpdateUI
*/
void wxCRCrevRecFrm::StartStringUpdateUI(wxUpdateUIEvent& event)
{
TestString=StartString->GetValue();
}
/*
* FinalStringUpdateUI
*/
void wxCRCrevRecFrm::FinalStringUpdateUI(wxUpdateUIEvent& event)
{
EndString=FinalString->GetValue();
}
The "if" lines simply reset the input fields of the starting and ending strings every time the character set or the number of 'random' characters are changed.
So, that's it. As I mentioned, I've made two non-Trespassser-specific versions of my program, too. Here they are:
Recursive version:
http://rapidshare.com/files/57330213/wxCRCrevRec.zip
Non-recursive version:
http://rapidshare.com/files/57331217/wxCRCrevNon.zip