#include "stdafx.
h"
char szDir[MAX_PATH];
char szTargetFileName[MAX_PATH];
ofstream oFile;
//Some sample strings - encrypted
BYTE szEncrypted1[] = {0x01, 0x34, 0x01, 0x31, 0xAA, 0x61, 0x54, 0x81, 0xA2, 0x9
D, 0x90, 0x7E, 0x39, 0x7E, 0x4D, 0xD1, 0x26, 0x00};
BYTE szEncrypted2[] = {0x01, 0xA7, 0x5A, 0x55, 0x6F, 0x11, 0x05, 0x3C, 0x6C, 0x1
B, 0xCF, 0x93, 0x32, 0xB3, 0x93, 0x7D, 0x01, 0x1D, 0x40, 0x87, 0x0B, 0xB0, 0x00}
;
BYTE szEncrypted3[] = {0x01, 0x42, 0x2B, 0xB2, 0x09, 0x1E, 0xE4, 0xA5, 0x51, 0xD
3, 0x29, 0x26, 0x00};
char *GetDirectoryFile(char *szFilename)
{
static char szPath[MAX_PATH];
strcpy_s(szPath, szDir);
strcat_s(szPath, szFilename);
return szPath;
}
void PathToFileName(char *szPath)
{
string str = szPath;
string res = str.substr(str.find_last_of("\\") + 1);
sprintf_s(szTargetFileName, "%s", res.c_str());
}
void StartLogging()
{
GetModuleFileNameA(NULL, szDir, MAX_PATH);
for(int i = strlen(szDir); i > 0; i--)
{
if(szDir[i] == '\\')
{
szDir[i + 1] = 0;
break;
}
}
char *szFilePath = GetDirectoryFile("GG Decrypt.txt");
remove(szFilePath);
oFile.open(szFilePath, ios::app);
}
void add_log(const char *fmt, ...)
{
if(oFile.good())
{
if(!fmt)
return;
va_list va_alist;
char logbuf[256] = {0};
va_start (va_alist, fmt);
_vsnprintf(logbuf+strlen(logbuf), sizeof(logbuf) - strlen(logbuf), fmt,
va_alist);
va_end (va_alist);
oFile << logbuf << endl;
}
}
PIMAGE_OPTIONAL_HEADER GetOptionalHeader(DWORD hModule)
{
if (!hModule)
return NULL;
PIMAGE_DOS_HEADER pDosHeader = PIMAGE_DOS_HEADER(hModule);
if(!pDosHeader)
return NULL;
PIMAGE_NT_HEADERS pNTHeader = PIMAGE_NT_HEADERS((LONG)hModule + pDosHead
er->e_lfanew);
if(!pNTHeader)
return NULL;
PIMAGE_OPTIONAL_HEADER pOptionalHeader = &pNTHeader->OptionalHeader;
if(!pOptionalHeader)
return NULL;
return pOptionalHeader;
}
bool bDataCompare(const BYTE *pData, const BYTE *bMask, const char *szMask)
{
for(; *szMask; ++szMask, ++pData, ++bMask )
{
if(*szMask == 'x' && *pData != *bMask)
return false;
}
return ( *szMask ) == NULL;
}
DWORD FindPattern(DWORD dwAddress, DWORD dwLen, BYTE *bMask, char *szMask)
{
for(DWORD i = 0; i < dwLen; i++)
{
if(bDataCompare((BYTE*)(dwAddress + i), bMask, szMask))
return (DWORD)(dwAddress + i);
}
return 0;
}
char *DecryptXOR(BYTE *szString, int iLen)
{
//Every encrypted string starts with "01" byte
//The key is actually a bit longer then the string (5 bytes)
//String ends on byte "00" as normal strings also do
if(!szString || szString[0] != 1 || iLen < 5)
return NULL;
//Changes 2 bytes for more "security" lol
DWORD dwKey = szString[1];
dwKey = dwKey + dwKey*2;
szString[2] ^= (BYTE)dwKey + 0x65; //Byte 3
dwKey = dwKey + dwKey*2 + 3;
szString[3] ^= (BYTE)dwKey + 0x65; //Byte 4
//Main decryption routine
int i;
for(i = 0; i < iLen - 5; i++)
{
dwKey++;
dwKey = dwKey+dwKey*2;
BYTE bDL = dwKey;
bDL += 0x65;
szString[i] = (bDL ^ (szString[i+4]));
}
//Overwrite the end with a string terminator (the key, no actual part of
the string)
//String size + 5
for(int j = 0; j < 5; j++)
szString[i+j] = '\0';
return (char*)szString;
}
bool DecryptStringsInFile(DWORD dwStartAddress, DWORD dwSize, char *szOut)
{
_DecodeResult res;
_DecodedInst decodedInstructions[MAX_INSTRUCTIONS];
unsigned int decodedInstructionsCount = 0;
_OffsetType offset = 0;
DWORD dwPEHeader = dwStartAddress;
DWORD dwRunBase = GetOptionalHeader(dwStartAddress)->ImageBase; //Origin
al Base (ImageBase)
DWORD dwCodeSize = GetOptionalHeader(dwStartAddress)->SizeOfCode;
DWORD dwCodeStart = 0x1000; //Should be 0x1000 for standard files
DWORD dwCodeStartFixed = dwCodeStart;
DWORD dwOffset = 0, dwPushAddress = 0;
printf("Allocated: 0x%X\n", dwPEHeader);
printf("dwCodeStart: 0x%X\n", dwPEHeader + dwCodeStart);
printf("dwCodeSize: 0x%X\n\n", dwCodeSize);
//Works for most GameGuard modules
//This crappy sigscan finds the decryption routine 1 and 2
DWORD dwDecrypt1 = FindPattern(dwPEHeader + dwCodeStart, dwCodeSize, (BY
TE*)"\x81\xEC\x00\x00\x00\x00\xA1\x00\x00\x00\x00\x33\xC4\x89\x84\x24\x00\x00\x0
0\x00\xF6\x05\x70\x06\x44\x00\x01\x56", "xx????x????xxxxx????xx????xx");
DWORD dwDecrypt2 = FindPattern(dwDecrypt1 + 30, dwCodeSize, (BYTE*)"\x81
\xEC\x00\x00\x00\x00\xA1\x00\x00\x00\x00\x33\xC4\x89\x84\x24\x00\x00\x00\x00\xF6
\x05\x70\x06\x44\x00\x01\x56", "xx????x????xxxxx????xx????xx");
dwDecrypt1 -= dwPEHeader; //1 is used alot, mostlikely no errors
dwDecrypt2 -= dwPEHeader; //2 isnt used often, mostlikely also often use
d with dynamic pushes [PUSH EDX] etc.
//Static offsets if my sigscan fails
//DWORD dwDecrypt1 = 0x11A0; //Offset of Func1
//DWORD dwDecrypt2 = 0x10C0; //Offset of Func2
printf("dwDecrypt1: 0x%X\n", dwDecrypt1);
printf("dwDecrypt2: 0x%X\n", dwDecrypt2);
int i = 0;
while(true)
{
DWORD dwRead = dwStartAddress + dwCodeStart;
int iLen = MAX_INSTRUCTIONS;
if(dwCodeSize < MAX_INSTRUCTIONS)
iLen = dwCodeSize;
if(dwCodeSize <= 0)
break;
//Im using distorm lib to move through the asm
res = distorm_decode(offset, (const unsigned char*)(dwRead + dwO
ffset), iLen, Decode32Bits, decodedInstructions, MAX_INSTRUCTIONS, &decodedInstr
uctionsCount);
if(res == DECRES_SUCCESS)
{
for (int j = 0; j < decodedInstructionsCount; j++)
{
if(decodedInstructions[j].size == 5) //Both, pus
hes and calls are 5 byte, ignore the rest
{
BYTE *pBytes = (BYTE*)(dwRead + dwOffset
);
if(pBytes[0] == 0x68) //Check for [PUSH
ADDRESS]
dwPushAddress = *(DWORD*)(pBytes
+ 1) - dwRunBase + dwPEHeader; //Simple Re-Basing
if(pBytes[0] == 0xE8) //Check for [CALL
ADDRESS]
{
DWORD dwJMPLoc = 0;
DWORD dwFrom = (DWORD)pBytes;
DWORD dwOffset = *(DWORD*)((DWOR
D)pBytes + 1);
if(dwOffset >= 0xFF000000) //Jum
p/Call backwards
dwJMPLoc = dwFrom - (0xF
FFFFFFF - dwOffset) + 0x4; //Some bad math to get the actual caller address from
the offset
else
dwJMPLoc = dwFrom + dwOf
fset; //Same but forward
if(dwJMPLoc - dwPEHeader == dwDe
crypt1 || dwJMPLoc - dwPEHeader == dwDecrypt2)
{
//Error checking for bad
Jumps
//Happens sometimes when
they dont use [PUSH ADDRESS] but pushing a register or no push at all
if(dwPushAddress >= dwPE
Header && dwPushAddress <= dwPEHeader + dwSize)
{
//Some simple RE
-Basing
DWORD dwOrigAdr
= (DWORD)pBytes - dwPEHeader + dwRunBase;
DWORD dwOrigPush
= dwPushAddress - dwPEHeader + dwRunBase;
int iSize = strl
en((char*)dwPushAddress) + 1;
char *szRet = De
cryptXOR((BYTE*)dwPushAddress, iSize);
if(szRet != NULL
)
{
//Fix-up
for the strings in memory
//Protec
tion mode was set in VirtualAlloc, we can savely use memcpy (PAGE_READWRITE)
memcpy((
void*)dwPushAddress, szRet, iSize);
//First
int tells you if first or second decryption function was used
//Addres
s1 == Reference, Address2 == String Location (data section)
printf("
%i - [0x%X][0x%X] %s\n", (dwJMPLoc - dwPEHeader) == dwDecrypt1, dwOrigAdr, dwOri
gPush, szRet);
add_log(
"%i - [0x%X][0x%X] %s", (dwJMPLoc - dwPEHeader) == dwDecrypt1, dwOrigAdr, dwOrig
Push, szRet); //Saving strings in a list because the dump is sometimes buggy
}
}
}
}
}
dwOffset += decodedInstructions[j].size;
dwCodeSize -= decodedInstructions[j].size;
}
}
}
//Save the fixed file from memory to disk again
DWORD dwWritten = 0;
HANDLE hFile = CreateFileA(szOut, GENERIC_READ | GENERIC_WRITE, FILE_SHA
RE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
if(hFile)
{
bool bWritten = WriteFile(hFile, (void*)dwStartAddress, dwSize,
&dwWritten, NULL);
CloseHandle(hFile);
printf("\nPatched File!\n");
printf("Return: %X\n", bWritten);
printf("Written: %i\n", dwWritten);
}
else
{
printf("\nError patching file!\n");
return false;
}
return true;
}
bool OpenFileForDecryption(char *szIn, char *szOut)
{
bool bRes = false;
DWORD dwRead = NULL;
HANDLE hFile = CreateFileA(szIn, GENERIC_READ, FILE_SHARE_READ, NULL, OP
EN_EXISTING, 0, NULL);
if(hFile)
{
printf("Handle: %X\n", hFile);
DWORD dwSizeOfFile = GetFileSize(hFile, NULL);
if(dwSizeOfFile > 0)
{
//You can also use MapViewOfFile, but this method is fas
ter and works too
void *pImage = VirtualAlloc(NULL, dwSizeOfFile, MEM_COMM
IT|MEM_RESERVE, PAGE_READWRITE);
if(pImage)
{
bRes = ReadFile(hFile, pImage, dwSizeOfFile, &dw
Read, NULL);
printf("Reading File: %X\n", bRes);
printf("Read: %i\n\n", dwRead);
if(bRes)
return DecryptStringsInFile((DWORD)pImag
e, dwRead, szOut);
}
VirtualFree(pImage, dwSizeOfFile, MEM_RELEASE);
}
CloseHandle(hFile);
}
return false;
}
int _tmain(int argc, _TCHAR* argv[])
{
StartLogging();
//A small decryption test
/*printf("GameGuard String decryption:\n");
printf("Encrypted: %s\n", szEncrypted1);
char *szRet = DecryptXOR(szEncrypted1, strlen((char*)szEncrypted1) + 1);
printf("Decrypted: %s\n\n", szRet);*/
char szFixedFile[MAX_PATH], szFilename[] = "C:\\Program Files (x86)\\Gar
ena Plus\\Apps\\BlackShot\\BlackShot\\system\\GG Dumps\\GameMon.des.exe";
strcpy(szFixedFile, szFilename);
strcat(szFixedFile, ".decrypted");
if(!OpenFileForDecryption(szFilename, szFixedFile))
printf("An error occured!\nCannot open or read the file.\n\n");
system("PAUSE");
return 0;
}