Last active
June 2, 2016 07:37
-
-
Save hasherezade/c5bd1a3e93bf66c648bc to your computer and use it in GitHub Desktop.
DeCrypter refactored
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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