/* Decrypts parts of 0x90.exe from honeynet.org's scan 33. gcc -o decrypt-0x90 -O2 -W -Wall decrypt-0x90.c peloy@chapus.net November 2004 */ #include #include #include #include #define IMAGE_BASE 0xde0000 #define CODE_BASE 0xde2000 #define MARKER1 0xdead #define MARKER2 0x31000 /* * The first decryption block in the first chain is at VMA 0xe2635b. Since * the file is loaded at address 0xde0000, the offset from the beginning of * the file to the first decryption block is at 0xe2635b - 0xde0000 = 287579. */ #define CHAIN1_BLOCK1 287579 /* * The first decryption block in the second chain is at VMA 0xe1c546. Since * the file is loaded at address 0xde0000, the offset from the beginning of * the file to the first decryption block is at 0xe1c546 - 0xde0000 = 247110. */ #define CHAIN2_BLOCK1 247110 struct decryption_block { char decrypt_code[155]; char nop_code[8]; char get_size_code[13]; char get_addr_code[14]; unsigned marker1; /* 0xdead */ unsigned code_offsets[4]; unsigned marker2; /* 0x31000 */ unsigned return_offsets[4]; } __attribute__((packed)); struct patch { unsigned address; char *data; unsigned size; } patches[] = { /* Bypass of all the decryption blocks */ {0xde2012, "\xe9\x3c\x66\x00\x00" /* jmp 0xde8653 */, 5}, /* Bypass NOP exception handlers after decryption blocks */ {0xde8658, "\xeb\x66" /* jmp 0xde86c0 */, 2}, {0xde86c5, "\xeb\x66" /* jmp 0xde872d */, 2}, /* Kill all these freaking exception handlers that change program flow */ {0xdea4b1 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe09aab, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe1b0b7, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe016e3, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe1146d, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xdec321 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xdee126 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe1815d + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe1b8e2 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe1550b + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xdf5989 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xdf74a6 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, /* jmp dword ptr ds:e1b9e9h[ecx*4] */ {0xe1a012 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe1b8e2 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe135bb + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe14c78 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe034b4 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe07940 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xdfdc5a + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xdf8fff + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xdfa2d0 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xdfb8ff + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, /* 0xe1b9f1 */ /* 0xe1b9f5 skipped */ {0xdf1776 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, /* 0xe1b9f9 */ {0xdfef93 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, /* 0xe1b9fd */ {0xe0b59e + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe0e7cd + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe12224 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe1736e + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe1b948 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe0f85e + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe0d473 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe0c680 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe0530a + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe0059c + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe03d21 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe08a08 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe10923 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe140df + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3}, {0xe15ac8 + 0x3f, "\xff\x24\x87" /* jmp dword ptr [edi+eax*4] */, 3} }; void decrypt_chain(struct decryption_block *d, char *buffer); void decrypt(char *buffer, unsigned size); void apply_patch(char *, struct patch *); char *program_name; /* For command line options */ int dryrun; char *fname; #define USAGE \ "\ndecrypt-0x90 1.0\n\n" \ "Usage: %s [-h] [-n] \n\n" \ "-h: this help\n" \ "-n: dry run. Do not update file after decrypting.\n" \ void usage(void) { fprintf(stderr, USAGE, program_name); exit(1); } int main(int argc, char **argv) { int c; FILE *f; long fsize; char *buffer; unsigned i; if ( (program_name = strrchr(argv[0], '/') ) ) program_name++; else program_name = argv[0]; while ( (c = getopt(argc, argv, "hn") ) != -1) switch (c) { case 'n': dryrun = 1; break; case 'h': default: usage(); break; } if (optind != argc - 1) { fprintf(stderr, "Need one argument (filename).\n"); exit(1); } fname = argv[optind]; f = fopen(fname, "r+"); if (f == NULL) { fprintf(stderr, "Error opening %s.\n", fname); exit(1); } /* * We read the entire file into memory, so we need to know its * size so we know how much memory we need to allocate. */ fseek(f, 0, SEEK_END); /* Move file pointer to the end of the file */ fsize = ftell(f); fseek(f, 0, SEEK_SET); /* Move file pointer to the beggining */ buffer = malloc(fsize); if (buffer == NULL) { fprintf(stderr, "Could not allocate memory.\n"); exit(1); } /* Read entire file into memory */ fread(buffer, 1, fsize, f); /* * Decrypt encrypted blocks (so far I've detected two chains.) */ decrypt_chain( (struct decryption_block *) &buffer[CHAIN1_BLOCK1], buffer); decrypt_chain( (struct decryption_block *) &buffer[CHAIN2_BLOCK1], buffer); /* Apply patches */ for (i = 0; i < sizeof(patches)/sizeof(patches[0]); i++) apply_patch(buffer, &patches[i]); if (!dryrun) { /* Now write to disk the stuff we just decrypted. */ if (fseek(f, 0, SEEK_SET) == -1) { fprintf(stderr, "Error seeking to beggining of file\n"); exit(1); } fwrite(buffer, 1, fsize, f); } fclose(f); free(buffer); return 0; } void decrypt(char *buffer, unsigned size) { do { *buffer++ ^= size; } while (--size); } void decrypt_chain(struct decryption_block *d, char *buffer) { unsigned nblocks; unsigned decrypt_len, decrypt_addr, ret_addr, prev_ret_addr; void *decrypt_start; for (nblocks = prev_ret_addr = 0; d->marker1 == MARKER1 && d->marker2 == MARKER2; d--, nblocks++) { decrypt_len = *(unsigned *) &d->get_size_code[1]; decrypt_addr = *(unsigned *) &d->get_addr_code[2]; ret_addr = *(unsigned *) &d->nop_code[3] + d->return_offsets[1] + CODE_BASE; if (prev_ret_addr == 0) prev_ret_addr = ret_addr; printf("block %d @0x%x: size=0x%08x, address=0x%08x, ret=0x%08x (delta=%d)\n", nblocks + 1, (unsigned) d - (unsigned) buffer + IMAGE_BASE, decrypt_len, decrypt_addr, ret_addr, ret_addr - prev_ret_addr); decrypt_start = buffer + decrypt_addr - IMAGE_BASE; decrypt(decrypt_start, decrypt_len); prev_ret_addr = ret_addr; } printf("\nTotal number of blocks in chain: %d\n\n", nblocks); } void apply_patch(char *buffer, struct patch *p) { printf("Patching 0x%08x\n", p->address); memcpy(&buffer[p->address - IMAGE_BASE], p->data, p->size); }