/* DON'T EVER WRITE CODE LIKE THIS! It demonstrates all kinds of bad coding practices. It's as wicked, as its authors mind (or a bit less) :-) It is full of bugs and it was designed to be compilable and work on my machine with my bfd, my libopcodes, not on yours :-) */ /* gcc decom.c -lbfd -lopcodes */ #include #include #include #include char *disbuf=NULL; static int myprint VPARAMS ((void *f, const char *format, ...)) { char *buf, *olddisbuf; VA_OPEN (args, format); VA_FIXEDARG (args, void *, f); VA_FIXEDARG (args, const char *, format); vasprintf (&buf, format, args); if (buf == NULL) exit(2); olddisbuf=disbuf; if (olddisbuf==NULL) asprintf (&disbuf, "%s", buf); else asprintf (&disbuf, "%s%s", olddisbuf, buf); free(buf); free(olddisbuf); VA_CLOSE (args); return 0; } bfd *b; asection *a; unsigned char *data; unsigned long datalen; struct disassemble_info dinfo; disassembler_ftype dis; unsigned long goto_start=0; int bigstop=0; int mydis(unsigned long eip, struct disassemble_info *dinfo) { int r; if (disbuf) free(disbuf); disbuf=NULL; r=dis(eip, dinfo); if (r<0) { printf("Unable to disassemble at 0x%x, aborting\n", eip); exit(4); } return r; } void blank(unsigned long from, unsigned long to) { unsigned long i; for (i=from-a->vma; ivma; i++) data[i]=0x90; } char *check_dummy(unsigned long *rva, int len, struct disassemble_info *dinfo) { unsigned long sum; unsigned long va; int i, l; char *olddisbuf; if (!strstr(disbuf, "pusha")) return NULL; sum=0; /* Is it the exception block? */ for (i=0; i<104; i++) sum=((sum<<13)|(sum>>19)+data[(*rva+len-1-a->vma)+i])^0xd0b3ded0; if (sum==0x7206d84) { char *q; asprintf(&q, "Exception block 0x%x-0x%x", *rva, *rva+len+102); blank(*rva, *rva+len+103); *rva+=len+103; return q; } sum=0; /* Is it the goto block? */ for (i=0; i<74; i++) sum=((sum<<13)|(sum>>19)+data[(*rva+len-1-a->vma)+i])^0xd0b3ded0; if (sum==0x70880ad) { char *q; asprintf(&q, "Exception goto block 0x%x-0x%x", goto_start, *rva+len+72); blank(goto_start, *rva+len+73); *rva+=len+73; /* Replacement goto code: pusha movzx eax,BYTE PTR [esi] mov edi,DWORD PTR [eax+0xe1b991] movzx eax,BYTE PTR [esi+1] mov exa,DWORD PTR [edi+eax] add eax, 9 (so we skip the prologue, except for the popa) jmp eax */ memcpy(data+goto_start-a->vma, "\x60\x0f\xb6\x06\x8b\x3c\x85\x91\xb9\xe1\x00\x0f\xb6\x46\x01\x8b\x04\x87\x83\xc0\x09\xff\xe0", 23); bigstop=1; goto_start=0; return q; } /* Is it the pusha/.../popa dummy code */ va=*rva+len; olddisbuf=disbuf; disbuf=NULL; while(1) { l=mydis(va, dinfo); if (strstr(disbuf, "popa")) { char *q; free(olddisbuf); asprintf(&q, "Dummy block 0x%x-0x%x", *rva, va+l-1); blank(*rva, va+l); *rva=va+l; return q; } else if (strstr(disbuf, "jmp")) { if (data[va-a->vma+l-2]!=0xeb) exit(3); va+=l+*(signed char*)(data+va-a->vma+l-1); } /* Dummy instructions are not allowed to use memory references, except for lea */ else if ((strstr(disbuf, "[") && !strstr(disbuf, "lea")) || strstr(disbuf,"pusha")) { disbuf=olddisbuf; return NULL; } else va+=l; } disbuf=olddisbuf; return NULL; } void display(unsigned long eip, int len) { int i; printf("%x: & ", eip); for (i=0; ivma+i]); for (; i<9; i++) printf(" "); for (i=0; disbuf[i]; i++) if (disbuf[i]==' ') break; for (; disbuf[i]; i++) if (disbuf[i]!=' ') break; i--; if (disbuf[i]==' ') { disbuf[i]='\0'; printf("& %s & %s \\\\\n", disbuf, disbuf+i+1); } else printf("& %s \\\\\n", disbuf); } /* void display(unsigned long eip, int len) { int i; printf("%x: ", eip); for (i=0; ivma+i]); for (; i<9; i++) printf(" "); printf("%s\n", disbuf); } */ void dedummify(unsigned long start, unsigned long end) { unsigned long eip, neip; int len; unsigned long etab1=0, etab2=0, encr_start=0; int stop=0; for (eip=start; eip>=start && eipvma)==0x06b60f60) break; /* Does it look like the beginning of an exception-al goto? */ else if (strstr(disbuf, "movzx") && strstr(disbuf, "eax,BYTE PTR [esi]")) { goto_start=eip; neip=eip+len; } /* Or is it the fixing code? */ /* Is it a pusha? If so, check for dummy code */ else if (strstr(disbuf, "pusha")) { char *q; neip=eip; if (q=check_dummy(&neip, len, &dinfo)) { printf("DUMMY (%s)\n", q); eip=neip; if (bigstop) { stop=1; bigstop=0; } continue; } else neip=eip+len; } /* Is it a call ? */ else if (strstr(disbuf, "call ")) { /* Is it the trivial call-pop sequence? */ display(eip, len); eip+=len; neip=eip+*(signed long*)(data+eip-a->vma-4); len=mydis(neip, &dinfo); if (!strstr(disbuf, "pop e")) dedummify(eip, end); eip=neip; continue; } /* Or a conditional jump ? */ else if (strstr(disbuf, "je ") || strstr(disbuf, "jne ")) { display(eip, len); if (data[eip-a->vma+len-2]==0x74 || data[eip-a->vma+len-2]==0x75) neip=eip+len+*(signed char*)(data+eip-a->vma+len-1); else neip=eip+len+*(signed long*)(data+eip-a->vma+len-4); dedummify(eip+len, end); eip=neip; continue; } else if (strstr(disbuf, "ret")) { stop=1; } else if (strstr(disbuf, "jmp")) { char *q=strstr(disbuf, "DWORD PTR [ecx"); if (q) { int i; unsigned long va=strtoul(q+17, 0, 0); neip=eip+len; for (i=0; start<= va+i && va+ivma+i); if (vaa=end) break; dedummify(vaa, end); } len=mydis(neip, &dinfo); } else if (data[eip-a->vma+len-2]==0xeb) neip=eip+len+*(signed char*)(data+eip-a->vma+len-1); else if (data[eip-a->vma+len-5]==0xe9) neip=eip+len+*(signed long*)(data+eip-a->vma+len-4); else { if (etab1!=0 && etab2!=0) { unsigned long ad=eip+len; int llen; do { llen=mydis(ad, &dinfo); } while(check_dummy(&ad, llen, &dinfo)); if (data[ad-a->vma+2]!=0x04) { llen=mydis(eip, &dinfo); stop=1; } else { int i, j; unsigned long start=0, leng=0, k; llen=mydis(eip, &dinfo); printf("Encryption using tables at 0x%x and 0x%x\n", etab1, etab2); for (i=1; i<=3; i++) { unsigned long va=*(unsigned long*)(data+etab1-a->vma+4*i)+a->vma; for (j=0; j<3; j++) { int l=mydis(va, &dinfo); if (strstr(disbuf, "lea")) { char *q=strstr(disbuf, "[ebp+"); if (!q) continue; start=strtoul(q+5, 0, 0); break; } else if (strstr(disbuf, "mov")) { char *q=strstr(disbuf, ","); if (!q) continue; leng=strtoul(q+1, 0, 0); break; } else if (strstr(disbuf, "ret")) break; va+=l; } if (j==3) exit(6); } if (start==0 || leng==0) exit(7); for (k=leng; k>0; k--, start++) data[start-a->vma]^=(k&0xff); etab1=0; etab2=0; len=mydis(ad+3, &dinfo); neip=ad+3+len; len=mydis(neip, &dinfo); if (data[neip-a->vma+len-2]==0xeb) eip=neip+len+*(signed char*)(data+neip-a->vma+len-1); else if (data[neip-a->vma+len-5]==0xe9) eip=neip+len+*(signed long*)(data+neip-a->vma+len-4); if (encr_start!=0) blank(encr_start, eip); encr_start=0; continue; } } } } else if (strstr(disbuf, "xor ")) { encr_start=eip; neip=eip+len; } else if (strstr(disbuf, "mov ")) { char *q=strstr(disbuf, "DWORD PTR [ebp+e"); if (q && strlen(q)>18) { unsigned long et=strtoul(q+21, 0, 10); if (etab1==0) { if (et>=start && etvma)==0xdead) etab1=et; } else if (etab2==0) { if (et>=start && etvma)==0x31000) etab2=et; } } neip=eip+len; } else if (strstr(disbuf, "nop")) { eip+=len; continue; } else { neip=eip+len; } display(eip, len); eip=neip; } } void dumpit() { FILE *f; char buf[300000]; int len; f=fopen("0x90-1.exe", "rb"); len=fread(buf, 1, sizeof(buf), f); fclose(f); memcpy(buf+0x2000, data, datalen); f=fopen("0x90-2.exe", "w+b"); fwrite(buf, 1, len, f); fclose(f); } void detable(unsigned long tva, unsigned long min, unsigned long max) { int i, j; for (i=0; tva+i+4<=max; i+=4) { unsigned long va0=*(unsigned long *)(data+tva-a->vma+i); if (va0=max) break; for (j=0; va0+j+4<=max; j+=4) { unsigned long va=*(unsigned long *)(data+va0-a->vma+j); if (min<=va && vavma]==0x64 || data[va-a->vma]==0x60)) { printf("Cleaning up function at %x\n", va); if (data[va-a->vma]==0x64) memset(data+va-a->vma, 0x90, 9); bigstop=0; dedummify(va, max); } } } } void reverse(unsigned long start, unsigned long base) { unsigned char t, i, j; unsigned long va; unsigned long x, y; va=start; while(1) { t=data[va-a->vma]; i=data[va-a->vma+1]; printf("%03x: ", va-base); switch(t) { case 0: switch(i) { case 0: x=*(unsigned long*)(data+va-a->vma+2)^0x37195411; printf("PUSH %04x\n", x); va+=6; break; case 1: x=*(unsigned long*)(data+va-a->vma+2)+0xadd01337; printf("PUSH %04x\n", x); va+=6; break; case 2: x=data[va-a->vma+2]^0x47; printf("PUSH R%d\n", x); va+=3; break; case 3: x=data[va-a->vma+2]^0x66; printf("POP R%d\n", x); va+=3; break; case 4: x=data[va-a->vma+2]^0x45; printf("ESP += %d\n", x); va+=3; break; default: printf("Unknown instruction subtype (%d/%d) !\n", t, i); return; } break; case 1: switch(i) { case 3: y=data[va-a->vma+2]; j=data[va-a->vma+3]; x=data[va-a->vma+4]; switch(j) { case 0: printf("XOR [R%d], BYTE(R%d)\n", x, y); break; case 1: printf("XOR [R%d], WORD(R%d)\n", x, y); break; case 2: printf("XOR [R%d], R%d\n", x, y); break; default: printf("Unknown XOR type (%d) !\n", j); } va+=5; break; case 4: printf("R%d += %04x\n", data[va-a->vma+2]-3, *(unsigned long*)(data+va-a->vma+3)); va+=7; break; case 5: printf("R%d -= %04x\n", data[va-a->vma+2]-2, *(unsigned long*)(data+va-a->vma+3)); va+=7; break; case 6: printf("R%d &= %04x\n", data[va-a->vma+2]-5, *(unsigned long*)(data+va-a->vma+3)); va+=7; break; case 7: printf("R%d |= %04x\n", data[va-a->vma+2]-4, *(unsigned long*)(data+va-a->vma+3)); va+=7; break; case 8: printf("R%d ^= %04x\n", data[va-a->vma+2], *(unsigned long*)(data+va-a->vma+3)); va+=7; break; case 9: printf("R%d += R%d\n", data[va-a->vma+3]-3, data[va-a->vma+2]-1); va+=4; break; case 10: printf("R%d ?= R%d\n", data[va-a->vma+3]-1, data[va-a->vma+2]-2); va+=4; break; default: printf("Unknown instruction subtype (%d/%d) !\n", t, i); return; } break; case 2: switch(i) { case 0: printf("Finish\n"); return; break; case 1: x=*(unsigned long*)(data+va-a->vma+2)+0xfea731de - 0xde1000; printf("API%d\n", (x-1)/4); va+=6; break; case 2: printf("R%d = %04x\n", data[va-a->vma+6], *(unsigned long*)(data+va-a->vma+2)+0xaefded04); va+=7; break; case 3: printf("R%d--\n", data[va-a->vma+2]); va+=3; break; case 4: printf("R%d++\n", data[va-a->vma+2]); va+=3; break; case 5: printf("R%d = 0\n", data[va-a->vma+2]); va+=3; break; case 6: printf("MemChr(R0, '\\x%02x', 0x%x)\n", data[va-a->vma+2], *(unsigned short*)(data+va-a->vma+3)); va+=5; break; case 7: printf("BreakPoint\n"); va+=2; break; case 8: printf("R%d = [R%d]\n", data[va-a->vma+3], data[va-a->vma+2]); va+=4; break; case 10: printf("R%d = Byte [R%d]\n", data[va-a->vma+3], data[va-a->vma+2]); va+=4; break; case 11: printf("R%d = Word [R%d]\n", data[va-a->vma+3], data[va-a->vma+2]); va+=4; break; default: printf("Unknown instruction subtype (%d/%d) !\n", t, i); return; } break; case 3: switch(i) { case 0: printf("StackCmpJe %04x\n", *(unsigned long*)(data+va-a->vma+2)-0x31337-base); va+=6; break; default: printf("Unknown instruction subtype (%d/%d) !\n", t, i); return; } break; case 4: switch(i) { case 0: x=*(unsigned long*)(data+va-a->vma+2)+0xdeadead; printf("JMP %04x\n", x-base); va=x; break; case 1: x=*(unsigned long*)(data+va-a->vma+2)+1; printf("JNZ %04x\n", x); /* Poor man solution to the infinite recursion problem */ if (x+base>va) reverse(x+base, base); va+=6; break; case 2: x=*(unsigned long*)(data+va-a->vma+2)+4; printf("JZ %04x\n", x); /* Poor man solution to the infinite recursion problem */ if (x+base>va) reverse(x+base, base); va+=6; break; default: printf("Unknown instruction subtype (%d/%d) !\n", t, i); return; } break; case 5: switch(i) { case 0: x=*(unsigned long*)(data+va-a->vma+2); printf("Call %04x (absolute %x)\n", x-base, x); reverse(x, base); va+=6; break; case 1: printf("Return\n", x); va+=2; return; break; default: printf("Unknown instruction subtype (%d/%d) !\n", t, i); return; } break; default: printf("Unknown instruction type (%d) !\n", t); return; } } } int main() { char **m; bfd_arch_info_type *ainfo; atexit(dumpit); bfd_init(); b=bfd_openr("0x90-1.exe", NULL); bfd_check_format_matches (b, bfd_object, &m); INIT_DISASSEMBLE_INFO(dinfo, stdout, fprintf); b->arch_info=bfd_scan_arch ("i386"); dis=disassembler(b); a=b->sections->next; /* We are interested only in second section */ dinfo.flavour = bfd_get_flavour (b); dinfo.arch = bfd_get_arch (b); dinfo.mach = bfd_get_mach (b); dinfo.disassembler_options = "intel"; dinfo.octets_per_byte = 1; datalen=dinfo.buffer_length=bfd_get_section_size_before_reloc(a); data=dinfo.buffer=(bfd_byte*)malloc(datalen); bfd_get_section_contents (b, a, data, 0, datalen); dinfo.buffer_vma=a->vma; dinfo.section=a; dinfo.fprintf_func=(fprintf_ftype)myprint; dinfo.stream=NULL; dedummify(a->vma, a->vma+dinfo.buffer_length); detable(0xe1b991,a->vma, a->vma+dinfo.buffer_length); { int i; for (i=0; i<0x20e; i++) data[0xe1ba65-a->vma+i]^=0x53; } { int i; for (i=0; i<0x49; i++) data[0xe1bc2a-a->vma+i]^=0x43; } reverse(0xe1ba3d, 0xe1ba3d); reverse(0xe1ba3d+0x11a, 0xe1ba3d); { int i; for (i=0; i<0x236; i++) printf("%s%02x(%c)", i&15 ? " ":"\n", data[0xe1ba3d-a->vma+i], isprint(data[0xe1ba3d-a->vma+i])?data[0xe1ba3d-a->vma+i]:'.'); } return 0; }