Skip to content

Instantly share code, notes, and snippets.

@hasherezade
Last active June 2, 2016 07:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hasherezade/c5bd1a3e93bf66c648bc to your computer and use it in GitHub Desktop.
Save hasherezade/c5bd1a3e93bf66c648bc to your computer and use it in GitHub Desktop.
DeCrypter refactored
// XOR Crypter decoder
// CC-BY: hasherezade
// for Malwarebytes
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _MSC_VER
#include <stdint.h>
#else
#include <inttypes.h>
#endif
#ifndef BYTE
typedef unsigned char BYTE;
#endif
#ifndef WORD
typedef uint16_t WORD;
#endif
#ifndef DWORD
typedef uint32_t DWORD;
#endif
const BYTE EXPECTED_OUTPUT[] = "GetProcAddress\x0\x0\x0"
"VirtualAlloc\x0\x0\x0\x0\x0"
"VirtualFree\x0\x0\x0\x0\x0\x0"
"UnmapViewOfFile\x0\x0"
"VirtualProtect\x0\x0\x0"
"LoadLibraryExA\x0\x0\x0"
"GetModuleHandleA\x0"
"CreateFileA\x0\x0\x0\x0\x0\x0"
"SetFilePointer\x0\x0\x0"
"WriteFile\x0\x0\x0\x0\x0\x0\x0\x0"
"CloseHandle\x0\x0\x0\x0\x0\x0"
"GetTempPathA\x0\x0\x0\x0\x0"
"lstrlenA\x0\x0\x0\x0\x0\x0\x0\x0\x0"
"lstrcatA\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0";
bool decode(DWORD *inbuf, //encrypted input
DWORD *outbuf, //buffer to store the output
size_t bufsize,
const DWORD key,
const size_t max_size = SIZE_MAX
)
{
if (inbuf == NULL || outbuf == NULL) return false;
for (size_t i = 0; i < bufsize; i++) {
DWORD val = inbuf[i];
DWORD step = i * sizeof(DWORD);
if (step >= max_size) {
outbuf[i] = val;
continue;
}
outbuf[i] = (val + step) ^ (key + step);
}
return true;
}
bool dump_to_file(BYTE *outbuf, size_t psize, const char *outfile_name)
{
FILE *outfile = fopen(outfile_name, "wb");
if (outfile == NULL) {
printf ("Opening output file failed!\n");
return false;
}
fwrite(outbuf, 1, psize, outfile);
fclose(outfile);
printf("Saved output as: %s\n", outfile_name);
return true;
}
DWORD find_stage1_key(DWORD *inbuf, //encrypted input
size_t bufsize
)
{
DWORD *out = (DWORD*) EXPECTED_OUTPUT;
size_t out_size = sizeof(EXPECTED_OUTPUT) / sizeof(DWORD);
if (inbuf == NULL || bufsize < out_size) return 0;
DWORD key = inbuf[0] ^ out[0];
//test key:
DWORD step = sizeof(DWORD);
DWORD key_res = (inbuf[1] + step) ^ (key + step);
if (key_res != out[1]) {
return 0;
}
return key;
}
size_t find_patterns(BYTE *fbuf, size_t fsize, WORD* patterns, WORD anti_pattern)
{
BYTE *buf = fbuf;
size_t bufsize = fsize;
BYTE *ptr1 = NULL;
size_t offset = 0;
while ((buf + sizeof(patterns)) < (fbuf + fsize)) {
ptr1 = (BYTE*) memchr((void*)buf, (int) patterns[0], bufsize);
if (ptr1 == NULL) {
return -1;
}
WORD *wPtr = (WORD*) ptr1;
if ((wPtr[2] == patterns[1] && wPtr[4] == patterns[2]) &&
(wPtr[1] != anti_pattern))
{
offset = (BYTE*)ptr1 - fbuf;
//printf("Got the pattern at: %#0x\n", offset);
break;
}
buf = ptr1 + sizeof(WORD);
size_t offset = (BYTE*)ptr1 - fbuf;
bufsize = fsize - offset;
}
offset = (BYTE*)ptr1 - fbuf - (sizeof(WORD));
return offset;
}
size_t find_chunk_beginning(BYTE *fbuf, size_t fsize, size_t offset)
{
WORD *eWords = (WORD*) EXPECTED_OUTPUT;
size_t wSize = offset / sizeof(WORD);
WORD patterns[] = {0,0,0};
patterns[0] = eWords[wSize + 1];
patterns[1] = eWords[wSize + 3];
patterns[2] = eWords[wSize + 5];
WORD anti_pattern = eWords[wSize + 2];
return find_patterns(fbuf,fsize, patterns, anti_pattern);
}
size_t find_chunk_size(BYTE *buf, size_t bufsize, DWORD key)
{
const size_t out_size = sizeof(EXPECTED_OUTPUT);
BYTE fragment[out_size];
memset(fragment, 0, out_size + 1);
if (!decode((DWORD*)(buf), (DWORD*)(fragment), out_size / sizeof(DWORD), key))
return -1;
for (size_t i = 0; i < out_size; i++) {
if (fragment[i] != EXPECTED_OUTPUT[i]) {
printf("Chunk size = %x\n", i);
return i;
}
}
return out_size;
}
size_t find_encrypted(BYTE *fbuf, size_t fsize, BYTE* outbuf)
{
size_t offset1 = find_chunk_beginning(fbuf, fsize, 0);
if (offset1 == -1) return -1;
DWORD key = find_stage1_key((DWORD*)(fbuf + offset1), (fsize - offset1) / sizeof(DWORD));
size_t chunk_size = find_chunk_size(fbuf + offset1, fsize - offset1, key);
size_t dif = 0;
if (chunk_size != sizeof(EXPECTED_OUTPUT)) {
size_t offset2 = find_chunk_beginning(fbuf, fsize, chunk_size);
if (offset2 == -1 || offset2 < offset1) {
return -1;
}
dif = (offset2 - offset1) - chunk_size;
}
BYTE *chunk_buf = fbuf + offset1;
size_t next_offset = offset1;
size_t out_offset = 0;
while (next_offset + chunk_size < fsize) {
memcpy(outbuf + out_offset, fbuf + next_offset, chunk_size);
next_offset += (chunk_size + dif);
out_offset += chunk_size;
}
return offset1;
}
bool decode_stage1(BYTE *inbuf, BYTE *outbuf, size_t fsize)
{
//prepare for stage#1 decoding:
size_t dwSize = fsize / sizeof(DWORD);
DWORD* dwPtr = (DWORD*) inbuf;
const DWORD key = find_stage1_key(dwPtr, dwSize);
if (key == 0) {
printf ("ERROR: Key not found!\n");
return false;
}
printf ("Stage#1 key: %#02x\n", key);
//stage#1:
if (!decode(dwPtr, (DWORD*)outbuf, dwSize, key)) {
printf ("ERROR: Decoding failed!\n");
return false;
}
return true;
}
bool decode_stage2(BYTE *inbuf, BYTE *outbuf, size_t fsize)
{
size_t dwSize = fsize / sizeof(DWORD);
DWORD* dwPtr = (DWORD*) inbuf;
const DWORD key = 0x03E9;
//stage#2:
if (!decode(dwPtr, (DWORD*)outbuf, dwSize, key)) {
printf ("Decoding failed!\n");
return false;
}
return true;
}
bool is_header_valid(BYTE *buf, size_t buf_size)
{
if (buf == NULL || buf_size < 2) {
return false;
}
if (buf[0] == 'M' && buf[1] == 'Z') {
return true;
}
return false;
}
DWORD read_payload_size(BYTE *buf, size_t fsize, const size_t psize_offset, const size_t payload_offset)
{
DWORD psize = *((DWORD*)(buf + psize_offset));
printf("Found payload size = %#02x = %d\n", psize, psize);
if (psize > (fsize - payload_offset)) {
printf("ERROR: invalid payload size!");
return 0;
}
return psize;
}
int main(int argc, char *argv[])
{
if (argc < 3) {
printf ("Expected parameters <input file name> <output file name>\n");
return -1;
}
FILE *p_file = fopen(argv[1], "rb");
fseek(p_file, 0, SEEK_END);
const size_t fsize = ftell(p_file);
fseek(p_file, 0, SEEK_SET);
BYTE* inbuf = (BYTE*) calloc(fsize, sizeof(BYTE));
BYTE* outbuf = (BYTE*) calloc(fsize, sizeof(BYTE));
fread(inbuf, 1, fsize, p_file);
fclose(p_file);
p_file = NULL;
//check:
size_t offset = find_encrypted(inbuf, fsize, outbuf);
if (offset == -1) {
printf("ERROR: Pattern not found! Probably not packed by this crypter!\n");
return -1;
}
memcpy(inbuf, outbuf, fsize);
memset(outbuf, 0, fsize);
if (!decode_stage1(inbuf, outbuf, fsize)) {
free(inbuf);
free(outbuf);
return -1;
}
//prepare for stage#2 decoding:
memset(inbuf, 0, fsize);
const size_t psize_offset = sizeof(EXPECTED_OUTPUT);
const size_t payload_offset = psize_offset + sizeof(DWORD);
DWORD psize = read_payload_size(outbuf, fsize, psize_offset, payload_offset);
if (psize == 0) {
printf("ERROR: invalid payload size!");
free(inbuf);
free(outbuf);
return -1;
}
memcpy(inbuf, outbuf + payload_offset, fsize - payload_offset);
if (!decode_stage2(inbuf, outbuf, (fsize - payload_offset))) {
free(inbuf);
free(outbuf);
return -1;
}
if (is_header_valid(outbuf, psize)) {
printf("Success! New executable found!\n");
} else {
printf("Decoded content is invalid!\n");
}
dump_to_file(outbuf, psize, argv[2]);
free(inbuf);
free(outbuf);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment