0% found this document useful (0 votes)
196 views

GameGuard StringDecryption

This C++ code is decrypting encrypted strings within an executable file. It uses pattern matching to find the decryption routines, then analyzes the assembly code to detect when strings are decrypted. It decrypts the strings in memory and saves the modified executable to disk.

Uploaded by

ZzcarloszZ
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
196 views

GameGuard StringDecryption

This C++ code is decrypting encrypted strings within an executable file. It uses pattern matching to find the decryption routines, then analyzes the assembly code to detect when strings are decrypted. It decrypts the strings in memory and saves the modified executable to disk.

Uploaded by

ZzcarloszZ
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 6

#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;
}

You might also like