/*

dcanalyzer.c

--

Analyzer for DarkComet Servers, make sure the server is fully unpacked before

using this tool. It will decrypt all known data, if it doesn't work you may be

working with a very old version of darkcomet and you should have a look at

the PE resources for the unencrypted data.

This tool is public domain. Tested under GCC and VS2010 with many darkcomet

versions.

*/

#define _BSD_SOURCE /* make gcc happy with snprintf as ansi */

#include <stdio.h>

#include <stdlib.h>

#include <stdint.h>

#include <string.h>

#ifdef WIN32

#include <Windows.h>

#endif

#if _MSC_VER

#define snprintf _snprintf /* work around for microsoft's "C" compiler */

#endif

/* We search the entire file for this string to find the RC4 key */

#define KEY_PREFIX "#KCMDDC"

int process ( char * file ) ;

void ShowOutput ( char * str ) ;

void process_resource_table ( int pos , char found , char * goal ) ;

FILE * fh ;

int32_t rsrc_pos ;

int32_t rsrc_virtual ;

char * dcstr = NULL ;

/* RC4 borrowed from FreeBSD */

struct rc4_state {

uint8_t perm [ 256 ] ;

uint8_t index1 ;

uint8_t index2 ;

} ;

static __inline void swap_bytes ( uint8_t * a , uint8_t * b )

{

uint8_t temp ;

temp = * a ;

* a = * b ;

* b = temp ;

}

void rc4_init ( struct rc4_state * state , uint8_t * key , int keylen )

{

uint8_t j ;

int i ;

/* Initialize state with identity permutation */

for ( i = 0 ; i < 256 ; i ++ )

state -> perm [ i ] = ( uint8_t ) i ;

state -> index1 = 0 ;

state -> index2 = 0 ;

/* Randomize the permutation using key data */

for ( j = i = 0 ; i < 256 ; i ++ ) {

j += state -> perm [ i ] + key [ i % keylen ] ;

swap_bytes ( & state -> perm [ i ] , & state -> perm [ j ] ) ;

}

}

void rc4_crypt ( struct rc4_state * state , uint8_t * inbuf , uint8_t * outbuf ,

int buflen )

{

int i ;

uint8_t j ;

for ( i = 0 ; i < buflen ; i ++ ) {

/* Update modification indicies */

state -> index1 ++;

state -> index2 += state -> perm [ state -> index1 ] ;

/* Modify permutation */

swap_bytes ( & state -> perm [ state -> index1 ] ,

& state -> perm [ state -> index2 ] ) ;

/* Encrypt/decrypt next byte */

j = state -> perm [ state -> index1 ] + state -> perm [ state -> index2 ] ;

outbuf [ i ] = inbuf [ i ] ^ state -> perm [ j ] ;

}

}

/* end FreeBSD RC4 code */

/* my PE header structs */

struct DATA_DIR

{

uint32_t addr ;

uint32_t size ;

} ;

struct RES_DIR_TABLE

{

uint32_t characteristics ;

uint32_t timestamp ;

uint16_t major ;

uint16_t minor ;

uint16_t num_name_elems ;

uint16_t num_id_elems ;

} ;

struct RES_DIR_ENTRY

{

union

{

uint32_t name_rva ;

uint32_t id ;

} identifier ;

union

{

uint32_t data_entry_rva ;

uint32_t subdir_rva ;

} child ;

} ;

struct RES_DATA

{

uint32_t data_rva ;

uint32_t size ;

uint32_t codepage ;

uint32_t reserved ;

} ;

void process_resource_entry ( struct RES_DIR_ENTRY entry , char found ,

char * goal )

{

struct RES_DATA data ;

if ( entry. child . data_entry_rva & 0x80000000 ) {

/* process subdir entry */

process_resource_table ( entry. child . subdir_rva & 0x7FFFFFFF ,

found , goal ) ;

} else {

if ( found ) {

fseek ( fh , rsrc_pos + entry. child . data_entry_rva ,

SEEK_SET ) ;

fread ( & data , sizeof ( data ) , 1 , fh ) ;

dcstr = ( char * ) malloc ( data. size + 1 ) ;

dcstr [ data. size ] = ' \0 ' ;

fseek ( fh , data. data_rva - rsrc_virtual + rsrc_pos ,

SEEK_SET ) ;

fread ( dcstr , 1 , data. size , fh ) ;

}

/* process data entry */

}

}

void process_resource_entry_id ( struct RES_DIR_ENTRY entry , char found ,

char * goal )

{

process_resource_entry ( entry , found , goal ) ;

}

char check_unicode_str ( char * goal , char * input , unsigned int len )

{

unsigned int i ;

char next ;

if ( strlen ( goal ) == len ) {

for ( i = 0 ; i < len * 2 ; i ++ ) {

next = ( i % 2 ) == 0 ? goal [ i / 2 ] : 0 ;

if ( input [ i ] != next )

return 0 ;

}

} else {

return 0 ;

}

return 1 ;

}

void process_resource_entry_name ( struct RES_DIR_ENTRY entry , char found ,

char * goal )

{

uint16_t name_len ;

char * name ;

fseek ( fh , rsrc_pos + ( entry. identifier . name_rva & 0x7FFFFFFF ) , SEEK_SET ) ;

fread ( & name_len , sizeof ( name_len ) , 1 , fh ) ;

name = ( char * ) malloc ( name_len * 2 + 1 ) ;

fread ( name , 1 , name_len * 2 , fh ) ;

if ( check_unicode_str ( goal , name , name_len ) ) {

process_resource_entry ( entry , 1 , goal ) ;

} else {

process_resource_entry ( entry , found , goal ) ;

}

}

void process_resource_table ( int pos , char found , char * goal )

{

struct RES_DIR_TABLE table ;

int i ;

int length ;

struct RES_DIR_ENTRY * entries ;

fseek ( fh , rsrc_pos + pos , SEEK_SET ) ;

fread ( & table , sizeof ( table ) , 1 , fh ) ;

length = sizeof ( struct RES_DIR_ENTRY ) *

( table. num_id_elems + table. num_name_elems ) ;

entries = ( struct RES_DIR_ENTRY * ) malloc ( length ) ;

fread ( entries , length , 1 , fh ) ;

for ( i = 0 ; i < table. num_name_elems ; i ++ ) {

process_resource_entry_name ( entries [ i ] , found , goal ) ;

}

for ( i = 0 ; i < table. num_id_elems ; i ++ ) {

process_resource_entry_id ( entries [ i ] , found , goal ) ;

}

}

char * decrypt_dcdata ( char * instr , char * key )

{

struct rc4_state rc4state ;

int i ;

int len = strlen ( instr ) / 2 ;

char * data ;

unsigned int tempdata ;

data = ( char * ) malloc ( len ) ;

for ( i = 0 ; i < len ; i ++ ) {

sscanf ( instr + ( i * 2 ) , "%2X" , & tempdata ) ;

data [ i ] = ( char ) tempdata ;

}

rc4_init ( & rc4state , ( uint8_t * ) key , strlen ( key ) ) ;

rc4_crypt ( & rc4state , ( uint8_t * ) data , ( uint8_t * ) data , len ) ;

data [ len ] = ' \0 ' ;

return data ;

}

int main ( int argc , char ** argv ) {

if ( argc < 2 ) {

ShowOutput ( "Please specify a file!" ) ;

return EXIT_FAILURE ;

}

return process ( argv [ 1 ] ) ;

}

#ifdef WIN32

int APIENTRY WinMain ( HINSTANCE hInstance , HINSTANCE hPrevInstance ,

LPSTR lpCmdLine , int nCmdShow )

{

char buffer [ 8192 ] ;

OPENFILENAME ofn ;

if ( __argc > 1 ) {

return main ( __argc , __argv ) ;

} else {

ZeroMemory ( & ofn , sizeof ( ofn ) ) ;

ofn. lStructSize = sizeof ( ofn ) ;

ofn. lpstrFile = buffer ;

ofn. lpstrFile [ 0 ] = ' \0 ' ;

ofn. nMaxFile = sizeof ( buffer ) ;

ofn. lpstrFilter = "DarkComet Server Executables \0 *.exe" ;

ofn. nFilterIndex = 1 ;

ofn. lpstrFileTitle = NULL ;

ofn. nMaxFileTitle = 0 ;

ofn. lpstrInitialDir = NULL ;

ofn. Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST ;

GetOpenFileName ( & ofn ) ;

return process ( ofn. lpstrFile ) ;

}

}

#endif

void ShowOutput ( char * str )

{

#ifdef WIN32

MessageBoxExA ( NULL , str , "DCAnalyzer" , 0 , 0 ) ;

#else

printf ( "%s

" , str ) ;

#endif

}

/* simple scanner to parse key out of executable */

char * find_key_by_force ( )

{

int pos = 0 ;

char * prefix = KEY_PREFIX ;

int max = strlen ( prefix ) ;

char * keybuf = ( char * ) malloc ( 4096 ) ;

char cur ;

fseek ( fh , 0 , SEEK_SET ) ; /* go to start of file */

do {

fread ( & cur , 1 , 1 , fh ) ;

if ( pos >= max ) {

keybuf [ pos ] = cur ;

if ( cur == ' \0 ' )

break ;

pos ++;

} else if ( cur == prefix [ pos ] ) {

keybuf [ pos ] = cur ;

pos ++;

} else {

pos = 0 ;

}

if ( pos == 4096 ) /* no heap overflow for you! */

break ;

} while ( ! feof ( fh ) ) ;

strncat ( keybuf , "890" , 4096 ) ; /* comes from a separate function */

return keybuf ;

}

int process ( char * file )

{

uint16_t mz_hdr ;

uint32_t pe_hdr_loc ;

uint32_t pe_hdr ;

uint16_t opt_hdr ;

uint32_t num_rva_sizes ;

uint32_t image_base ;

unsigned int opt_hdr_pos ;

unsigned int i ;

char * output ;

char buffer [ 1024 ] ;

char final_buffer [ 8192 ] ;

char section_name [ 9 ] ;

char found ;

int successful ;

char * key ;

/* We search for these when we can't find the new DCDATA resource.

Used for old versions of DarkComet */

char * fallbacks [ ] = { "FWB" , "GENCODE" , "MUTEX" , "NETDATA" , "OFFLINEK" ,

"SID" , "FTPUPLOADK" , "FTPHOST" , "FTPUSER" , "FTPPASS" , "FTPPORT" ,

"FTPSIZE" , "FTPROOT" , "PWD" } ;

fh = fopen ( file , "rb" ) ;

if ( fh == NULL ) {

ShowOutput ( "File open failed" ) ;

return EXIT_FAILURE ;

}

key = find_key_by_force ( ) ;

fseek ( fh , 0 , SEEK_SET ) ; /* go to start of file */

section_name [ 8 ] = ' \0 ' ;

fread ( & mz_hdr , sizeof ( int16_t ) , 1 , fh ) ; /* read first 2 bytes of file */

if ( mz_hdr != * ( uint16_t * ) "MZ" ) {

ShowOutput ( "Not an MZ Executable!" ) ;

return EXIT_FAILURE ;

}

fseek ( fh , 0x3C , SEEK_SET ) ;

fread ( & pe_hdr_loc , sizeof ( pe_hdr_loc ) , 1 , fh ) ; /* offset of PE header */

fseek ( fh , pe_hdr_loc , SEEK_SET ) ;

fread ( & pe_hdr , sizeof ( pe_hdr ) , 1 , fh ) ;

if ( pe_hdr != * ( uint32_t * ) "PE \0 \0 " ) {

ShowOutput ( "Not a PE Executable!" ) ;

return EXIT_FAILURE ;

}

opt_hdr_pos = pe_hdr_loc + 0x18 ;

fseek ( fh , opt_hdr_pos , SEEK_SET ) ;

fread ( & opt_hdr , sizeof ( opt_hdr ) , 1 , fh ) ;

if ( opt_hdr != 0x010B ) {

ShowOutput ( "Invalid optional header!" ) ;

return EXIT_FAILURE ;

}

fseek ( fh , opt_hdr_pos + 28 , SEEK_SET ) ; /* ImageBase so we can subtract it later */

fread ( & image_base , sizeof ( image_base ) , 1 , fh ) ;

fseek ( fh , opt_hdr_pos + 92 , SEEK_SET ) ; /* NumberOfRvaAndSizes */

fread ( & num_rva_sizes , sizeof ( num_rva_sizes ) , 1 , fh ) ;

if ( num_rva_sizes > 0x10 )

num_rva_sizes = 0x10 ; /* enforce this so dumb tricks cannot work */

fseek ( fh , num_rva_sizes * 8 , SEEK_CUR ) ;

/* we should now be at the section headers */

found = 0 ;

do {

fread ( section_name , 1 , 8 , fh ) ;

if ( strncmp ( ".rsrc" , section_name , 8 ) == 0 ) {

fseek ( fh , 4 , SEEK_CUR ) ;

fread ( & rsrc_virtual , 4 , 1 , fh ) ;

fseek ( fh , 4 , SEEK_CUR ) ;

fread ( & rsrc_pos , 4 , 1 , fh ) ;

found ++;

fseek ( fh , 16 , SEEK_CUR ) ; /* skip rest of this section header. */

} else {

fseek ( fh , 40 - 8 , SEEK_CUR ) ; /* skip rest of this section header. */

}

} while ( found != 1 ) ;

process_resource_table ( 0 , 0 , "DCDATA" ) ;

if ( dcstr != NULL ) {

ShowOutput ( decrypt_dcdata ( dcstr , key ) ) ;

} else {

final_buffer [ 0 ] = ' \0 ' ;

successful = 0 ;

for ( i = 0 ; i < ( sizeof ( fallbacks ) / sizeof ( * fallbacks ) ) ; i ++ ) {

process_resource_table ( 0 , 0 , fallbacks [ i ] ) ;

if ( dcstr != NULL ) {

output = decrypt_dcdata ( dcstr , key ) ;

if ( final_buffer [ 0 ] == ' \0 ' )

snprintf ( buffer , 1024 , "%s: %s" , fallbacks [ i ] , output ) ;

else

snprintf ( buffer , 1024 , "

%s: %s" , fallbacks [ i ] , output ) ;

strncat ( final_buffer , buffer , 8192 ) ;

successful ++;

dcstr = NULL ; /* null it every round */

}

}

if ( successful )

ShowOutput ( final_buffer ) ;

else

ShowOutput ( "Could not find any DarkComet resource!

" ) ;

}

return EXIT_SUCCESS ;