Skip to content

Instantly share code, notes, and snippets.

@spnow
Forked from toufik-airane/XORStrings.c
Created January 3, 2017 14:08
Show Gist options
  • Save spnow/df43185e7c23922ae43854f71c78d3d8 to your computer and use it in GitHub Desktop.
Save spnow/df43185e7c23922ae43854f71c78d3d8 to your computer and use it in GitHub Desktop.
/*
2013/03/08
XORStrings V0.0.1, look for XOR, ROL or SHIFT encoded strings in a file
Source code put in public domain by Didier Stevens, no Copyright
https://DidierStevens.com
Use at your own risk
Shortcommings, or todo's ;-)
- file must fit in memory
- unicode support
History:
2013/02/26: start, fork from XORSearch V1.8
2013/03/01: added l and t options
2013/03/02: added c option
2013/03/03: added ParseHexArg
2013/03/08: added options o and k; refactoring
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#define MAXPATH 1024
#define OPR_XOR 0
#define OPR_ROL 1
#define OPR_SHIFT 2
char *apszOperations[3] = {"XOR", "ROL", "SHIFT"};
struct RESULT
{
int iOperation;
int iKey;
int iCountStrings;
int iCountCharacters;
int iMaxStringLength;
long lIndexMaxString;
};
struct RESULT asResults[512];
long ParseNumericArg(const char *szArg)
{
char *szError;
long lResult;
lResult = strtol(szArg, &szError, 0);
if (*szError != '\0' || lResult == LONG_MIN || lResult == LONG_MAX)
return -1;
else
return lResult;
}
long ParseHexArg(const char *szArg)
{
long lResult = 0;
while (*szArg != '\0')
{
if (*szArg >= '0' && *szArg <= '9')
lResult = lResult * 0x10 + *szArg - '0';
else if (*szArg >= 'a' && *szArg <= 'f')
lResult = lResult * 0x10 + *szArg - 'a' + 10;
else if (*szArg >= 'A' && *szArg <= 'F')
lResult = lResult * 0x10 + *szArg - 'A' + 10;
else
return -1;
if (lResult == LONG_MIN || lResult == LONG_MAX)
return -1;
szArg++;
}
return lResult;
}
int ParseArgs(int argc, char **argv, char **ppcFile, int *piSave, int *piUnicode, int *piDump, int *piMaxSort, int *piMinimumStringLength, int *piTerminator, int *piCSV, int *piOperation, int *piKey)
{
int iIterArgv;
int iCountParameters;
char *pcFlags;
int iFlagMinimumStringLength;
int iFlagTerminator;
int iFlagOperation;
int iFlagKey;
iCountParameters = 0;
iFlagMinimumStringLength = 0;
iFlagTerminator = 0;
iFlagOperation = 0;
iFlagKey = 0;
*piSave = 0;
*piUnicode = 0;
*piDump = 0;
*piMaxSort = 0;
*piMinimumStringLength = -1;
*piTerminator = -1;
*piCSV = 0;
*piOperation = -1;
*piKey = -1;
for (iIterArgv = 1; iIterArgv < argc; iIterArgv++)
{
if (argv[iIterArgv][0] == '-')
{
if (iFlagMinimumStringLength || iFlagTerminator || iFlagOperation || iFlagKey)
return 1;
pcFlags = argv[iIterArgv] + 1;
while (*pcFlags)
switch (*pcFlags++)
{
case 's':
*piSave = 1;
break;
case 'u':
*piUnicode = 1;
break;
case 'd':
*piDump = 1;
break;
case 'm':
*piMaxSort = 1;
break;
case 'l':
iFlagMinimumStringLength = 1;
break;
case 't':
iFlagTerminator = 1;
break;
case 'c':
*piCSV = 1;
break;
case 'o':
iFlagOperation = 1;
break;
case 'k':
iFlagKey = 1;
break;
default:
return 1;
}
}
else if (iFlagMinimumStringLength)
{
*piMinimumStringLength = ParseNumericArg(argv[iIterArgv]);
if (*piMinimumStringLength < 1)
return 1;
iFlagMinimumStringLength = 0;
}
else if (iFlagTerminator)
{
if (!strncmp(argv[iIterArgv], "0x", 2))
*piTerminator = ParseHexArg(argv[iIterArgv] + 2);
else
*piTerminator = ParseNumericArg(argv[iIterArgv]);
if (*piTerminator == -1)
return 1;
iFlagTerminator = 0;
}
else if (iFlagOperation)
{
int iIter;
for (iIter = 0; iIter <= OPR_SHIFT; iIter++)
if (!strcmp(argv[iIterArgv], apszOperations[iIter]))
*piOperation = iIter;
if (*piOperation == -1)
return 1;
iFlagOperation = 0;
}
else if (iFlagKey)
{
if (!strncmp(argv[iIterArgv], "0x", 2))
*piKey = ParseHexArg(argv[iIterArgv] + 2);
else
*piKey = ParseNumericArg(argv[iIterArgv]);
if (*piKey == -1)
return 1;
iFlagKey = 0;
}
else if (iCountParameters == 0)
{
*ppcFile = argv[iIterArgv];
iCountParameters++;
}
else
iCountParameters++;
}
if (iCountParameters != 1)
return 1;
else
return 0;
}
void XOR(unsigned char *pcBuffer, long lSize, unsigned char cXOR)
{
unsigned char *pcBufferEnd;
pcBufferEnd = pcBuffer + lSize;
while (pcBuffer < pcBufferEnd)
*pcBuffer++ ^= cXOR;
}
void ROL(unsigned char *pcBuffer, long lSize)
{
unsigned char *pcBufferEnd;
pcBufferEnd = pcBuffer + lSize;
while (pcBuffer < pcBufferEnd)
{
*pcBuffer = *pcBuffer << 1 | *pcBuffer >> 7;
pcBuffer++;
}
}
void SHIFTL(unsigned char *pcBuffer, long lSize)
{
unsigned char *pcBufferEnd;
unsigned char ucFirstBit;
pcBufferEnd = pcBuffer + lSize;
ucFirstBit = *pcBuffer >> 7;
while (pcBuffer < pcBufferEnd - 1)
{
*pcBuffer = *pcBuffer << 1 | *(pcBuffer + 1) >> 7;
pcBuffer++;
}
*(pcBufferEnd - 1) = *(pcBufferEnd - 1) << 1 | ucFirstBit;
}
void SHIFTR(unsigned char *pcBuffer, long lSize)
{
unsigned char *pcBufferIter;
unsigned char ucLastBit;
pcBufferIter = pcBuffer + lSize - 1;
ucLastBit = *pcBufferIter & 0x01;
while (pcBuffer < pcBufferIter)
{
*pcBufferIter = *pcBufferIter >> 1 | *(pcBufferIter - 1) << 7;
pcBufferIter--;
}
*pcBuffer = *pcBuffer >> 1 | ucLastBit << 7;
}
void SaveFile(const char *pcFile, const char *sOperation, unsigned char ucXOR, void *pBuffer, long lSize)
{
char szFileNameSave[MAXPATH];
FILE *fOut;
snprintf(szFileNameSave, MAXPATH, "%s.%s.%02X", pcFile, sOperation, ucXOR);
if ((fOut = fopen(szFileNameSave, "wb")) == NULL)
fprintf(stderr, "error opening file %s\n", szFileNameSave);
else
{
if (fwrite(pBuffer, lSize, 1, fOut) != 1)
fprintf(stderr, "error writing file %s\n", szFileNameSave);
fclose (fOut);
}
}
int IsPrintable(unsigned char ucByte)
{
return ucByte >= 0x20 && ucByte <= 0x7E;
}
void StringsAnalysis(const unsigned char *pucBuffer, long lSize, struct RESULT *psResult, int iMinimumStringLength, unsigned char ucTerminator)
{
long lIter;
long lStringStart = -1;
int iCountStrings = 0;
int iCountCharacters = 0;
int iMaxStringLength = 0;
long lIndexMaxString= -1;
for (lIter = 0; lIter < lSize; lIter++)
{
if (pucBuffer[lIter] != ucTerminator && IsPrintable(pucBuffer[lIter]))
{
if (lStringStart == -1)
lStringStart = lIter;
}
else if (lStringStart != -1)
{
if (pucBuffer[lIter] == ucTerminator && lIter - lStringStart >= iMinimumStringLength)
{
iCountStrings++;
if (iMaxStringLength < lIter - lStringStart)
{
iMaxStringLength = lIter - lStringStart;
lIndexMaxString = lStringStart;
}
iCountCharacters += lIter - lStringStart;
}
lStringStart = -1;
}
}
psResult->iCountStrings = iCountStrings;
psResult->iCountCharacters = iCountCharacters;
psResult->iMaxStringLength = iMaxStringLength;
psResult->lIndexMaxString = lIndexMaxString;
}
char *OPRString(int iOperation)
{
if (iOperation <= OPR_SHIFT)
return apszOperations[iOperation];
else
return "ERROR";
}
int CompareStringCounts(const void *a, const void *b)
{
const struct RESULT *resulta = (struct RESULT *) a;
const struct RESULT *resultb = (struct RESULT *) b;
return (resulta->iCountStrings > resultb->iCountStrings) - (resulta->iCountStrings < resultb->iCountStrings);
}
int CompareStringMaxLengths(const void *a, const void *b)
{
const struct RESULT *resulta = (struct RESULT *) a;
const struct RESULT *resultb = (struct RESULT *) b;
return (resulta->iMaxStringLength > resultb->iMaxStringLength) - (resulta->iMaxStringLength < resultb->iMaxStringLength);
}
void StringsPrint(const unsigned char *pucBuffer, long lSize, int iMinimumStringLength, unsigned char ucTerminator)
{
long lIter;
long lStringStart = -1;
for (lIter = 0; lIter < lSize; lIter++)
{
if (pucBuffer[lIter] != ucTerminator && IsPrintable(pucBuffer[lIter]))
{
if (lStringStart == -1)
lStringStart = lIter;
}
else if (lStringStart != -1)
{
if (pucBuffer[lIter] == ucTerminator && lIter - lStringStart >= iMinimumStringLength)
printf("%.*s\n", (int)(lIter - lStringStart), pucBuffer + lStringStart);
lStringStart = -1;
}
}
}
void DoStringsAnalysis(void *pBuffer, long lSize, const char *pcArgFile, int iFlagSave, int iFlagDump, int iFlagMaxSort, int iMinimumStringLength, int iTerminator, int iFlagCSV)
{
unsigned char ucOPRIter;
int iCountResults = 0;
int iIter;
ucOPRIter = 0;
do
{
XOR((unsigned char *) pBuffer, lSize, ucOPRIter);
asResults[iCountResults].iOperation = OPR_XOR;
asResults[iCountResults].iKey = ucOPRIter;
StringsAnalysis(pBuffer, lSize, &asResults[iCountResults++], iMinimumStringLength, (unsigned char) iTerminator);
if (iFlagSave && asResults[iCountResults - 1].iCountStrings > 0)
SaveFile(pcArgFile, OPRString(OPR_XOR), ucOPRIter, pBuffer, lSize);
XOR((unsigned char *) pBuffer, lSize, ucOPRIter);
} while (++ucOPRIter);
for (ucOPRIter = 1; ucOPRIter < 8; ucOPRIter++)
{
ROL((unsigned char *) pBuffer, lSize);
asResults[iCountResults].iOperation = OPR_ROL;
asResults[iCountResults].iKey = ucOPRIter;
StringsAnalysis(pBuffer, lSize, &asResults[iCountResults++], iMinimumStringLength, (unsigned char) iTerminator);
if (iFlagSave && asResults[iCountResults - 1].iCountStrings > 0)
SaveFile(pcArgFile, OPRString(OPR_ROL), ucOPRIter, pBuffer, lSize);
}
ROL((unsigned char *) pBuffer, lSize);
for (ucOPRIter = 1; ucOPRIter < 8; ucOPRIter++)
{
SHIFTL((unsigned char *) pBuffer, lSize);
asResults[iCountResults].iOperation = OPR_SHIFT;
asResults[iCountResults].iKey = ucOPRIter;
StringsAnalysis(pBuffer, lSize, &asResults[iCountResults++], iMinimumStringLength, (unsigned char) iTerminator);
if (iFlagSave && asResults[iCountResults - 1].iCountStrings > 0)
SaveFile(pcArgFile, OPRString(OPR_SHIFT), ucOPRIter, pBuffer, lSize);
}
for (ucOPRIter = 1; ucOPRIter < 8; ucOPRIter++)
SHIFTR((unsigned char *) pBuffer, lSize);
if (iFlagMaxSort)
qsort(asResults, iCountResults, sizeof(struct RESULT), CompareStringMaxLengths);
else
qsort(asResults, iCountResults, sizeof(struct RESULT), CompareStringCounts);
puts(iFlagCSV ? "Opr,Key,Count,Avg,Max" : "Opr Key Count Avg Max");
for (iIter = 0; iIter < iCountResults; iIter++)
{
if (asResults[iIter].iCountStrings != 0)
{
printf(iFlagCSV ? "%s,0x%02x,%d,%.1f,%d" : "%5s 0x%02x %5d %5.1f %5d", OPRString(asResults[iIter].iOperation), asResults[iIter].iKey, asResults[iIter].iCountStrings, asResults[iIter].iCountCharacters * 1.0 / asResults[iIter].iCountStrings, asResults[iIter].iMaxStringLength);
if (iFlagDump)
switch (asResults[iIter].iOperation)
{
case OPR_XOR:
XOR((unsigned char *) pBuffer, lSize, asResults[iIter].iKey);
printf(iFlagCSV ? ",\"%.*s\"\n" : " %.*s\n", asResults[iIter].iMaxStringLength, (char *)pBuffer + asResults[iIter].lIndexMaxString);
XOR((unsigned char *) pBuffer, lSize, asResults[iIter].iKey);
break;
case OPR_ROL:
for (ucOPRIter = 0; ucOPRIter < asResults[iIter].iKey; ucOPRIter++)
ROL((unsigned char *) pBuffer, lSize);
printf(iFlagCSV ? ",\"%.*s\"\n" : " %.*s\n", asResults[iIter].iMaxStringLength, (char *)pBuffer + asResults[iIter].lIndexMaxString);
for (ucOPRIter = 0; ucOPRIter < (8 - asResults[iIter].iKey); ucOPRIter++)
ROL((unsigned char *) pBuffer, lSize);
break;
case OPR_SHIFT:
for (ucOPRIter = 0; ucOPRIter < asResults[iIter].iKey; ucOPRIter++)
SHIFTL((unsigned char *) pBuffer, lSize);
printf(iFlagCSV ? ",\"%.*s\"\n" : " %.*s\n", asResults[iIter].iMaxStringLength, (char *)pBuffer + asResults[iIter].lIndexMaxString);
for (ucOPRIter = 0; ucOPRIter < asResults[iIter].iKey; ucOPRIter++)
SHIFTR((unsigned char *) pBuffer, lSize);
break;
}
else
puts("");
}
}
}
void DoStringsPrint(void *pBuffer, long lSize, int iOperation, int iKey, int iMinimumStringLength, int iTerminator, const char *pcArgFile, int iFlagSave)
{
int iIter = iKey;
switch (iOperation)
{
case OPR_XOR:
XOR((unsigned char *) pBuffer, lSize, iKey);
break;
case OPR_ROL:
while (iIter--)
ROL((unsigned char *) pBuffer, lSize);
break;
case OPR_SHIFT:
while (iIter--)
SHIFTL((unsigned char *) pBuffer, lSize);
break;
}
StringsPrint(pBuffer, lSize, iMinimumStringLength, (unsigned char) iTerminator);
if (iFlagSave)
SaveFile(pcArgFile, OPRString(iOperation), iKey, pBuffer, lSize);
}
int CheckArgumentsAndOptions(const char *pcArgFile, int iFlagDump, int iFlagMaxSort, int iTerminator, int iFlagCSV, int iOperation, int iKey)
{
if (strlen(pcArgFile) >= MAXPATH - 1)
{
fprintf(stderr, "Error: filename is too long\n");
return -1;
}
if (iTerminator < 0x00 || iTerminator > 0xFF)
{
fprintf(stderr, "Error: terminator out of range\n");
return -1;
}
if (iOperation == -1 && iKey != -1 || iOperation != -1 && iKey == -1)
{
fprintf(stderr, "Error: flags -o and -k must be used together\n");
return -1;
}
if (iOperation != -1 && iKey != -1 && (iFlagDump || iFlagMaxSort || iFlagCSV))
{
fprintf(stderr, "Error: flags -o and -k can't be used with flags -d, -m or -c\n");
return -1;
}
return 0;
}
int StoreFileContentInMemory(const char *pcArgFile, void **ppBuffer, long *plSize)
{
FILE *fIn;
struct stat statFile;
if (stat(pcArgFile, &statFile) != 0)
{
fprintf(stderr, "error opening file %s\n", pcArgFile);
return -1;
}
if ((*ppBuffer = malloc(statFile.st_size)) == NULL)
{
fprintf (stderr, "file %s is too large %ld\n", pcArgFile, (unsigned long)statFile.st_size);
return -1;
}
if ((fIn = fopen(pcArgFile, "rb")) == NULL)
{
fprintf(stderr, "error opening file %s\n", pcArgFile);
free (*ppBuffer);
return -1;
}
if (fread(*ppBuffer, statFile.st_size, 1, fIn) != 1)
{
fprintf(stderr, "error reading file %s\n", pcArgFile);
fclose (fIn);
free (*ppBuffer);
return -1;
}
fclose (fIn);
*plSize = statFile.st_size;
return 0;
}
main(int argc, char **argv)
{
void *pBuffer;
char *pcArgFile;
int iFlagSave;
int iFlagUnicode;
int iFlagDump;
int iFlagMaxSort;
int iMinimumStringLength;
int iTerminator;
int iFlagCSV;
int iOperation;
int iKey;
long lSize;
if (ParseArgs(argc, argv, &pcArgFile, &iFlagSave, &iFlagUnicode, &iFlagDump, &iFlagMaxSort, &iMinimumStringLength, &iTerminator, &iFlagCSV, &iOperation, &iKey))
{
fprintf(stderr, "Usage: XORStrings [options] file\n"
"XORStrings V0.0.1, look for XOR, ROL or SHIFT encoded strings in a file\n"
"Use -s to save the XOR, ROL or SHIFT encoded file\n"
// "Use -u to search for Unicode strings (limited support)\n"
"Use -d to dump the longest string\n"
"Use -m sort by maximum string length\n"
"Use -l to set the minimum string length (default 5)\n"
"Use -t to set the string terminator character, accepts integer or hex number (default 0)\n"
"Use -c to output CSV\n"
"Use -o to select the operation (XOR, ROL or SHIFT) to perform (to be used together with -k)\n"
"Use -k to select the key for the operation to perform (to be used together with -o)\n"
"Source code put in the public domain by Didier Stevens, no Copyright\n"
"Use at your own risk\n"
"https://DidierStevens.com\n");
return -1;
}
// Set defaults
if (iMinimumStringLength == -1)
iMinimumStringLength = 5;
if (iTerminator == -1)
iTerminator = 0;
if (-1 == CheckArgumentsAndOptions(pcArgFile, iFlagDump, iFlagMaxSort, iTerminator, iFlagCSV, iOperation, iKey))
return -1;
if (-1 == StoreFileContentInMemory(pcArgFile, &pBuffer, &lSize))
return -1;
if (iOperation == -1 && iKey == -1)
DoStringsAnalysis(pBuffer, lSize, pcArgFile, iFlagSave, iFlagDump, iFlagMaxSort, iMinimumStringLength, iTerminator, iFlagCSV);
else
DoStringsPrint(pBuffer, lSize, iOperation, iKey, iMinimumStringLength, iTerminator, pcArgFile, iFlagSave);
free(pBuffer);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment