Advertisement
Guest User

dcanalyzer.c

a guest
Jul 6th, 2012
3,638
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.06 KB | None | 0 0
  1. /*
  2. dcanalyzer.c
  3. --
  4. Analyzer for DarkComet Servers, make sure the server is fully unpacked before
  5. using this tool. It will decrypt all known data, if it doesn't work you may be
  6. working with a very old version of darkcomet and you should have a look at
  7. the PE resources for the unencrypted data.
  8.  
  9. This tool is public domain. Tested under GCC and VS2010 with many darkcomet
  10. versions.
  11. */
  12. #define _BSD_SOURCE /* make gcc happy with snprintf as ansi */
  13.  
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <stdint.h>
  17. #include <string.h>
  18.  
  19. #ifdef WIN32
  20. #include <Windows.h>
  21. #endif
  22.  
  23. #if _MSC_VER
  24. #define snprintf _snprintf /* work around for microsoft's "C" compiler */
  25. #endif
  26.  
  27. /* We search the entire file for this string to find the RC4 key */
  28. #define KEY_PREFIX "#KCMDDC"
  29.  
  30.  
  31. int process(char * file);
  32. void ShowOutput(char * str);
  33. void process_resource_table(int pos, char found, char * goal);
  34.  
  35. FILE * fh;
  36. int32_t rsrc_pos;
  37. int32_t rsrc_virtual;
  38. char * dcstr = NULL;
  39.  
  40. /* RC4 borrowed from FreeBSD */
  41. struct rc4_state {
  42.     uint8_t perm[256];
  43.     uint8_t index1;
  44.     uint8_t index2;
  45. };
  46.  
  47. static __inline void swap_bytes(uint8_t *a, uint8_t *b)
  48. {
  49.     uint8_t temp;
  50.     temp = *a;
  51.     *a = *b;
  52.     *b = temp;
  53. }
  54.  
  55. void rc4_init(struct rc4_state * state, uint8_t *key, int keylen)
  56. {
  57.     uint8_t j;
  58.     int i;
  59.  
  60.     /* Initialize state with identity permutation */
  61.     for (i = 0; i < 256; i++)
  62.         state->perm[i] = (uint8_t)i;
  63.     state->index1 = 0;
  64.     state->index2 = 0;
  65.  
  66.     /* Randomize the permutation using key data */
  67.     for (j = i = 0; i < 256; i++) {
  68.         j += state->perm[i] + key[i % keylen];
  69.         swap_bytes(&state->perm[i], &state->perm[j]);
  70.     }
  71. }
  72.  
  73. void rc4_crypt(struct rc4_state * state, uint8_t *inbuf, uint8_t *outbuf,
  74.     int buflen)
  75. {
  76.     int i;
  77.     uint8_t j;
  78.  
  79.     for (i = 0; i < buflen; i++) {
  80.  
  81.         /* Update modification indicies */
  82.         state->index1++;
  83.         state->index2 += state->perm[state->index1];
  84.  
  85.         /* Modify permutation */
  86.         swap_bytes(&state->perm[state->index1],
  87.             &state->perm[state->index2]);
  88.  
  89.         /* Encrypt/decrypt next byte */
  90.         j = state->perm[state->index1] + state->perm[state->index2];
  91.         outbuf[i] = inbuf[i] ^ state->perm[j];
  92.     }
  93. }
  94. /* end FreeBSD RC4 code */
  95.  
  96. /* my PE header structs */
  97. struct DATA_DIR
  98. {
  99.     uint32_t addr;
  100.     uint32_t size;
  101. };
  102.  
  103. struct RES_DIR_TABLE
  104. {
  105.     uint32_t characteristics;
  106.     uint32_t timestamp;
  107.     uint16_t major;
  108.     uint16_t minor;
  109.     uint16_t num_name_elems;
  110.     uint16_t num_id_elems;
  111. };
  112.  
  113. struct RES_DIR_ENTRY
  114. {
  115.     union
  116.     {
  117.         uint32_t name_rva;
  118.         uint32_t id;
  119.     } identifier;
  120.     union
  121.     {
  122.         uint32_t data_entry_rva;
  123.         uint32_t subdir_rva;
  124.     } child;
  125. };
  126.  
  127. struct RES_DATA
  128. {
  129.     uint32_t data_rva;
  130.     uint32_t size;
  131.     uint32_t codepage;
  132.     uint32_t reserved;
  133. };
  134.  
  135.  
  136.  
  137. void process_resource_entry(struct RES_DIR_ENTRY entry, char found,
  138.     char * goal)
  139. {
  140.     struct RES_DATA data;
  141.    
  142.     if (entry.child.data_entry_rva & 0x80000000) {
  143.         /* process subdir entry */
  144.         process_resource_table(entry.child.subdir_rva & 0x7FFFFFFF,
  145.             found, goal);
  146.     } else {
  147.         if (found) {
  148.             fseek(fh, rsrc_pos + entry.child.data_entry_rva,
  149.                 SEEK_SET);
  150.             fread(&data, sizeof(data), 1, fh);
  151.             dcstr = (char*) malloc(data.size+1);
  152.             dcstr[data.size] = '\0';
  153.             fseek(fh, data.data_rva - rsrc_virtual + rsrc_pos,
  154.                 SEEK_SET);
  155.             fread(dcstr, 1, data.size, fh);
  156.         }
  157.         /* process data entry */
  158.     }
  159. }
  160.  
  161. void process_resource_entry_id(struct RES_DIR_ENTRY entry, char found,
  162.     char * goal)
  163. {
  164.     process_resource_entry(entry, found, goal);
  165. }
  166.  
  167. char check_unicode_str(char * goal, char * input, unsigned int len)
  168. {
  169.     unsigned int i;
  170.     char next;
  171.     if (strlen(goal) == len) {
  172.         for (i = 0; i < len*2; i++) {
  173.             next = (i % 2) == 0 ? goal[i/2] : 0;
  174.             if (input[i] != next)
  175.                 return 0;
  176.         }
  177.     } else {
  178.         return 0;
  179.     }
  180.     return 1;
  181. }
  182.  
  183. void process_resource_entry_name(struct RES_DIR_ENTRY entry, char found,
  184.     char * goal)
  185. {
  186.     uint16_t name_len;
  187.     char * name;
  188.  
  189.     fseek(fh, rsrc_pos + (entry.identifier.name_rva & 0x7FFFFFFF), SEEK_SET);
  190.     fread(&name_len, sizeof(name_len), 1, fh);
  191.  
  192.     name = (char*) malloc(name_len*2 + 1);
  193.     fread(name, 1, name_len*2, fh);
  194.  
  195.     if (check_unicode_str(goal, name, name_len)) {
  196.         process_resource_entry(entry, 1, goal);
  197.     } else {
  198.         process_resource_entry(entry, found, goal);
  199.     }  
  200. }
  201.  
  202. void process_resource_table(int pos, char found, char * goal)
  203. {
  204.     struct RES_DIR_TABLE table;
  205.     int i;
  206.     int length;
  207.     struct RES_DIR_ENTRY * entries;
  208.  
  209.     fseek(fh, rsrc_pos + pos, SEEK_SET);
  210.     fread(&table, sizeof(table), 1, fh);
  211.     length = sizeof(struct RES_DIR_ENTRY) *
  212.         (table.num_id_elems + table.num_name_elems);
  213.     entries = (struct RES_DIR_ENTRY*) malloc(length);
  214.     fread(entries, length, 1, fh);
  215.  
  216.     for (i = 0; i < table.num_name_elems; i++) {
  217.         process_resource_entry_name(entries[i], found, goal);
  218.     }
  219.  
  220.     for (i = 0; i < table.num_id_elems; i++) {
  221.         process_resource_entry_id(entries[i], found, goal);
  222.     }
  223. }
  224.  
  225. char * decrypt_dcdata(char * instr, char * key)
  226. {
  227.     struct rc4_state rc4state;
  228.     int i;
  229.     int len = strlen(instr)/2;
  230.     char * data;
  231.     unsigned int tempdata;
  232.     data = (char *)malloc(len);
  233.    
  234.     for (i = 0; i < len; i++) {
  235.         sscanf(instr+(i*2), "%2X", &tempdata);
  236.         data[i] = (char) tempdata;
  237.     }
  238.     rc4_init(&rc4state, (uint8_t*)key, strlen(key));
  239.     rc4_crypt(&rc4state, (uint8_t*)data, (uint8_t*)data, len);
  240.     data[len] = '\0';
  241.     return data;
  242. }
  243.  
  244. int main(int argc, char** argv) {
  245.  
  246.     if (argc < 2) {
  247.         ShowOutput("Please specify a file!");
  248.         return EXIT_FAILURE;
  249.     }
  250.  
  251.     return process(argv[1]);
  252. }
  253.  
  254. #ifdef WIN32
  255. int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  256.     LPSTR lpCmdLine, int nCmdShow)
  257. {
  258.     char buffer[8192];
  259.     OPENFILENAME ofn;
  260.     if (__argc > 1) {
  261.         return main(__argc, __argv);
  262.     } else {
  263.         ZeroMemory( &ofn , sizeof( ofn));
  264.         ofn.lStructSize = sizeof(ofn);
  265.         ofn.lpstrFile = buffer;
  266.         ofn.lpstrFile[0] = '\0';
  267.         ofn.nMaxFile = sizeof(buffer);
  268.         ofn.lpstrFilter = "DarkComet Server Executables\0*.exe";
  269.         ofn.nFilterIndex = 1;
  270.         ofn.lpstrFileTitle = NULL;
  271.         ofn.nMaxFileTitle = 0;
  272.         ofn.lpstrInitialDir = NULL;
  273.         ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  274.         GetOpenFileName(&ofn);
  275.  
  276.         return process(ofn.lpstrFile);
  277.     }
  278. }
  279. #endif
  280.  
  281. void ShowOutput(char * str)
  282. {
  283. #ifdef WIN32
  284.         MessageBoxExA(NULL, str, "DCAnalyzer", 0, 0);
  285. #else
  286.         printf("%s\n", str);
  287. #endif
  288. }
  289.  
  290. /* simple scanner to parse key out of executable */
  291. char * find_key_by_force()
  292. {
  293.     int pos = 0;
  294.     char * prefix = KEY_PREFIX;
  295.     int max = strlen(prefix);
  296.     char * keybuf = (char*) malloc(4096);
  297.     char cur;
  298.     fseek(fh, 0, SEEK_SET); /* go to start of file */
  299.    
  300.     do {
  301.         fread(&cur, 1, 1, fh);
  302.        
  303.         if (pos >= max) {
  304.             keybuf[pos] = cur;
  305.             if (cur == '\0')
  306.                 break;
  307.             pos++;
  308.         } else if (cur == prefix[pos]) {
  309.             keybuf[pos] = cur;
  310.             pos++;
  311.         } else {
  312.             pos = 0;
  313.         }
  314.  
  315.         if (pos == 4096) /* no heap overflow for you! */
  316.             break;
  317.     } while (!feof(fh));
  318.  
  319.     strncat(keybuf, "890", 4096); /* comes from a separate function */
  320.  
  321.     return keybuf;
  322. }
  323.  
  324. int process(char * file)
  325. {
  326.     uint16_t mz_hdr;
  327.     uint32_t pe_hdr_loc;
  328.     uint32_t pe_hdr;
  329.     uint16_t opt_hdr;
  330.     uint32_t num_rva_sizes;
  331.     uint32_t image_base;
  332.     unsigned int opt_hdr_pos;
  333.     unsigned int i;
  334.     char * output;
  335.     char buffer[1024];
  336.     char final_buffer[8192];
  337.     char section_name[9];
  338.     char found;
  339.     int successful;
  340.     char * key;
  341.     /* We search for these when we can't find the new DCDATA resource.
  342.        Used for old versions of DarkComet */
  343.     char * fallbacks[] = {"FWB", "GENCODE", "MUTEX", "NETDATA", "OFFLINEK",
  344.         "SID", "FTPUPLOADK", "FTPHOST", "FTPUSER", "FTPPASS", "FTPPORT",
  345.         "FTPSIZE", "FTPROOT", "PWD"};
  346.  
  347.     fh = fopen(file, "rb");
  348.     if (fh == NULL) {
  349.         ShowOutput("File open failed");
  350.         return EXIT_FAILURE;
  351.     }
  352.  
  353.     key = find_key_by_force();
  354.  
  355.     fseek(fh, 0, SEEK_SET); /* go to start of file */
  356.  
  357.     section_name[8] = '\0';
  358.  
  359.     fread(&mz_hdr, sizeof(int16_t), 1, fh); /* read first 2 bytes of file */
  360.     if (mz_hdr != *(uint16_t*)"MZ") {
  361.         ShowOutput("Not an MZ Executable!");
  362.         return EXIT_FAILURE;
  363.     }
  364.  
  365.     fseek(fh, 0x3C, SEEK_SET);
  366.     fread(&pe_hdr_loc, sizeof(pe_hdr_loc), 1, fh); /* offset of PE header */
  367.    
  368.     fseek(fh, pe_hdr_loc, SEEK_SET);
  369.     fread(&pe_hdr, sizeof(pe_hdr), 1, fh);
  370.  
  371.     if (pe_hdr != *(uint32_t*)"PE\0\0") {
  372.         ShowOutput("Not a PE Executable!");
  373.         return EXIT_FAILURE;
  374.     }
  375.  
  376.     opt_hdr_pos = pe_hdr_loc + 0x18;
  377.     fseek(fh, opt_hdr_pos, SEEK_SET);
  378.     fread(&opt_hdr, sizeof(opt_hdr), 1, fh);
  379.  
  380.     if (opt_hdr != 0x010B) {
  381.         ShowOutput("Invalid optional header!");
  382.         return EXIT_FAILURE;
  383.     }
  384.  
  385.     fseek(fh, opt_hdr_pos + 28, SEEK_SET); /* ImageBase so we can subtract it later */
  386.     fread(&image_base, sizeof(image_base), 1, fh);
  387.  
  388.     fseek(fh, opt_hdr_pos + 92, SEEK_SET); /*  NumberOfRvaAndSizes */
  389.     fread(&num_rva_sizes, sizeof(num_rva_sizes), 1, fh);
  390.  
  391.     if (num_rva_sizes > 0x10)
  392.         num_rva_sizes = 0x10; /* enforce this so dumb tricks cannot work */
  393.    
  394.     fseek(fh, num_rva_sizes*8, SEEK_CUR);
  395.    
  396.     /* we should now be at the section headers */
  397.     found = 0;
  398.     do {
  399.         fread(section_name, 1, 8, fh);
  400.         if (strncmp(".rsrc", section_name, 8) == 0) {
  401.             fseek(fh, 4, SEEK_CUR);
  402.             fread(&rsrc_virtual, 4, 1, fh);
  403.             fseek(fh, 4, SEEK_CUR);
  404.             fread(&rsrc_pos, 4, 1, fh);
  405.             found++;
  406.             fseek(fh, 16, SEEK_CUR);   /* skip rest of this section header. */
  407.         } else  {
  408.             fseek(fh, 40-8, SEEK_CUR); /* skip rest of this section header. */
  409.         }
  410.     } while (found != 1);
  411.  
  412.     process_resource_table(0,0, "DCDATA");
  413.  
  414.     if (dcstr != NULL) {
  415.         ShowOutput(decrypt_dcdata(dcstr, key));
  416.     } else {
  417.         final_buffer[0] = '\0';
  418.         successful = 0;
  419.         for (i = 0; i < (sizeof(fallbacks)/sizeof(*fallbacks)); i++) {
  420.             process_resource_table(0,0, fallbacks[i]);
  421.             if (dcstr != NULL) {
  422.                 output = decrypt_dcdata(dcstr, key);
  423.                 if (final_buffer[0] == '\0')
  424.                     snprintf(buffer, 1024, "%s: %s", fallbacks[i], output);
  425.                 else
  426.                     snprintf(buffer, 1024, "\n%s: %s", fallbacks[i], output);
  427.                 strncat(final_buffer, buffer, 8192);
  428.                 successful++;
  429.                 dcstr = NULL; /* null it every round */
  430.             }
  431.         }
  432.         if (successful)
  433.  
  434.             ShowOutput(final_buffer);
  435.         else
  436.             ShowOutput("Could not find any DarkComet resource!\n");
  437.  
  438.     }
  439.    
  440.     return EXIT_SUCCESS;
  441. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement