/* * unlzexe -- uncompresses DOS executables (compressed with LZEXE) * under OpenBSD (and possibly other Unices as well) * * 32-bit refreshed version by Anders Gavare * * This is practically a port from UNLZEXE for DOS. The original * notes follow below my notes. It will compile under gcc (2.8.1) * but is not compatible with 16-bit systems anymore. * * Why would anyone want to unpack DOS executables under Unix? you * might ask. Well, one answer could be to make it easier to read * texts in DOS executables... if you need to do that for some reason. * * 0.9G 16 Mar 1999 Converting to OpenBSD (refreshing pretty * much all of the code). The output is * a little bit corrupt, though, because * we have 32-bit pointers now, not 16-bit... * Keeping v0.8 and v0.7 comments... * 0.9G2 17 Mar 1999 Refreshing more. Fixed bug. I've not tried * to actually execute the resulting DOS * binary, though :) */ /* unlzexe ver 0.5 (PC-VAN UTJ44266 Kou ) * UNLZEXE converts the compressed file by lzexe(ver.0.90,0.91) to the * UNcompressed executable one. * * usage: UNLZEXE packedfile[.EXE] [unpackedfile.EXE] v0.6 David Kirschbaum, Toad Hall, kirsch@usasoc.soc.mil, Jul 91 Problem reported by T.Salmi (ts@uwasa.fi) with UNLZEXE when run with TLB-V119 on 386's. Stripping out the iskanji and isjapan() stuff (which uses a somewhat unusual DOS interrupt) to see if that's what's biting us. -- Found it, thanks to Dan Lewis (DLEWIS@SCUACC.SCU.EDU). Silly us: didn't notice the "r.h.al=0x3800;" in isjapan(). Oh, you don't see it either? INT functions are called with AH having the service. Changing to "r.x.ax=0x3800;". v0.7 Alan Modra, amodra@sirius.ucs.adelaide.edu.au, Nov 91 Fixed problem with large files by casting ihead components to long in various expressions. Fixed MinBSS & MaxBSS calculation (ohead[5], ohead[6]). Now UNLZEXE followed by LZEXE should give the original file. v0.8 Vesselin Bontchev, bontchev@fbihh.informatik.uni-hamburg.de, Aug 92 Fixed recognition of EXE files - both 'MZ' and 'ZM' in the header are recognized. Recognition of compressed files made more robust - now just patching the 'LZ90' and 'LZ91' strings will not fool the program. */ #include #include #include #define VERSION "0.9G2" #define FAILURE 1 #define SUCCESS 0 typedef unsigned short WORD; typedef unsigned char BYTE; char *tmpfname = "$tmpfil$.exe"; char *backup_ext = ".olz"; char ipath[FILENAME_MAX], opath[FILENAME_MAX], ofname[FILENAME_MAX]; int main (int argc, char *argv[]) { int fnamechk (char*, char*, char*, int, char **); int fnamechg (char*, char*, char*, int); int rdhead (FILE *, int *); int mkreltbl (FILE *, FILE *,int); int unpack (FILE *, FILE *); void wrhead (FILE *); FILE *ifile,*ofile; int ver, rename_sw=0; printf ("unlzexe v"VERSION"\n"); if (argc!=3 && argc!=2) { printf ("usage: %s packedfile [unpackedfile]\n", argv[0]); exit (EXIT_FAILURE); } if (argc==2) rename_sw=1; if (fnamechk(ipath,opath,ofname,argc,argv)!=SUCCESS) exit(EXIT_FAILURE); if (!(ifile=fopen(ipath,"rb"))) { perror (ipath); exit (EXIT_FAILURE); } if (rdhead(ifile,&ver)!=SUCCESS){ printf("'%s' is not LZEXE file.\n",ipath); fclose(ifile); exit(EXIT_FAILURE); } if (!(ofile=fopen(opath,"w+b"))) { perror (opath); fclose (ifile); exit (EXIT_FAILURE); } printf ("%s is compressed by LZEXE v0.%d\n", ipath, ver); if (mkreltbl (ifile,ofile,ver)!=SUCCESS) { fclose (ifile); fclose (ofile); remove (opath); exit (EXIT_FAILURE); } if(unpack (ifile,ofile)!=SUCCESS) { fclose (ifile); fclose (ofile); remove (opath); exit (EXIT_FAILURE); } fclose (ifile); wrhead (ofile); fclose (ofile); if (fnamechg(ipath,opath,ofname,rename_sw)!=SUCCESS) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); return 0; } void parsepath(char *pathname, int *fname, int *ext); /* file name check */ int fnamechk (char *ipath, char *opath, char *ofname, int argc, char *argv[]) { int idx_name,idx_ext; strcpy (ipath, argv[1]); parsepath (ipath, &idx_name, &idx_ext); if (!ipath[idx_ext]) strcpy(ipath+idx_ext,".exe"); if (!strcasecmp(ipath+idx_name,tmpfname)) { printf("%s: bad filename\n",ipath); return(FAILURE); } if (argc==2) strcpy (opath, ipath); else strcpy (opath, argv[2]); parsepath(opath,&idx_name,&idx_ext); if (! opath[idx_ext]) strcpy(opath+idx_ext,".exe"); if (!strcasecmp(opath+idx_ext,backup_ext)) { printf("%s: bad filename.\n", opath); return(FAILURE); } strncpy (ofname,opath+idx_name,12); strcpy (opath+idx_name,tmpfname); return (SUCCESS); } int fnamechg (char *ipath, char *opath, char *ofname, int rename_sw) { int idx_name, idx_ext; char tpath[FILENAME_MAX]; if (rename_sw) { strcpy(tpath,ipath); parsepath(tpath,&idx_name,&idx_ext); strcpy(tpath+idx_ext,backup_ext); remove(tpath); if (rename(ipath,tpath)) { printf ("can't make %s\n", tpath); remove (opath); return (FAILURE); } printf("%s renamed to %s\n", ipath, tpath); } strcpy (tpath,opath); parsepath (tpath,&idx_name,&idx_ext); strcpy (tpath+idx_name,ofname); remove (tpath); if (rename(opath,tpath)) { if (rename_sw) { strcpy (tpath,ipath); parsepath (tpath,&idx_name,&idx_ext); strcpy (tpath+idx_ext,backup_ext); rename (tpath,ipath); } printf ("can't make '%s'. unpacked file '%s' is remained.\n", tpath, tmpfname); return (FAILURE); } printf ("unpacked file '%s' is generated.\n",tpath); return (SUCCESS); } void parsepath(char *pathname, int *fname, int *ext) { char c; int i; *fname=0; *ext=0; for(i=0;c=pathname[i];i++) switch(c) { case ':' : case '\\': *fname=i+1; break; case '.' : *ext=i; break; default : ; } if(*ext<=*fname) *ext=i; } /*-------------------------------------------*/ static WORD ihead[0x10], ohead[0x10], inf[8]; static long loadsize; static BYTE sig90 [] = { /* v0.8 */ 0x06, 0x0E, 0x1F, 0x8B, 0x0E, 0x0C, 0x00, 0x8B, 0xF1, 0x4E, 0x89, 0xF7, 0x8C, 0xDB, 0x03, 0x1E, 0x0A, 0x00, 0x8E, 0xC3, 0xB4, 0x00, 0x31, 0xED, 0xFD, 0xAC, 0x01, 0xC5, 0xAA, 0xE2, 0xFA, 0x8B, 0x16, 0x0E, 0x00, 0x8A, 0xC2, 0x29, 0xC5, 0x8A, 0xC6, 0x29, 0xC5, 0x39, 0xD5, 0x74, 0x0C, 0xBA, 0x91, 0x01, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0xFF, 0x4C, 0xCD, 0x21, 0x53, 0xB8, 0x53, 0x00, 0x50, 0xCB, 0x2E, 0x8B, 0x2E, 0x08, 0x00, 0x8C, 0xDA, 0x89, 0xE8, 0x3D, 0x00, 0x10, 0x76, 0x03, 0xB8, 0x00, 0x10, 0x29, 0xC5, 0x29, 0xC2, 0x29, 0xC3, 0x8E, 0xDA, 0x8E, 0xC3, 0xB1, 0x03, 0xD3, 0xE0, 0x89, 0xC1, 0xD1, 0xE0, 0x48, 0x48, 0x8B, 0xF0, 0x8B, 0xF8, 0xF3, 0xA5, 0x09, 0xED, 0x75, 0xD8, 0xFC, 0x8E, 0xC2, 0x8E, 0xDB, 0x31, 0xF6, 0x31, 0xFF, 0xBA, 0x10, 0x00, 0xAD, 0x89, 0xC5, 0xD1, 0xED, 0x4A, 0x75, 0x05, 0xAD, 0x89, 0xC5, 0xB2, 0x10, 0x73, 0x03, 0xA4, 0xEB, 0xF1, 0x31, 0xC9, 0xD1, 0xED, 0x4A, 0x75, 0x05, 0xAD, 0x89, 0xC5, 0xB2, 0x10, 0x72, 0x22, 0xD1, 0xED, 0x4A, 0x75, 0x05, 0xAD, 0x89, 0xC5, 0xB2, 0x10, 0xD1, 0xD1, 0xD1, 0xED, 0x4A, 0x75, 0x05, 0xAD, 0x89, 0xC5, 0xB2, 0x10, 0xD1, 0xD1, 0x41, 0x41, 0xAC, 0xB7, 0xFF, 0x8A, 0xD8, 0xE9, 0x13, 0x00, 0xAD, 0x8B, 0xD8, 0xB1, 0x03, 0xD2, 0xEF, 0x80, 0xCF, 0xE0, 0x80, 0xE4, 0x07, 0x74, 0x0C, 0x88, 0xE1, 0x41, 0x41, 0x26, 0x8A, 0x01, 0xAA, 0xE2, 0xFA, 0xEB, 0xA6, 0xAC, 0x08, 0xC0, 0x74, 0x40, 0x3C, 0x01, 0x74, 0x05, 0x88, 0xC1, 0x41, 0xEB, 0xEA, 0x89 }, sig91 [] = { 0x06, 0x0E, 0x1F, 0x8B, 0x0E, 0x0C, 0x00, 0x8B, 0xF1, 0x4E, 0x89, 0xF7, 0x8C, 0xDB, 0x03, 0x1E, 0x0A, 0x00, 0x8E, 0xC3, 0xFD, 0xF3, 0xA4, 0x53, 0xB8, 0x2B, 0x00, 0x50, 0xCB, 0x2E, 0x8B, 0x2E, 0x08, 0x00, 0x8C, 0xDA, 0x89, 0xE8, 0x3D, 0x00, 0x10, 0x76, 0x03, 0xB8, 0x00, 0x10, 0x29, 0xC5, 0x29, 0xC2, 0x29, 0xC3, 0x8E, 0xDA, 0x8E, 0xC3, 0xB1, 0x03, 0xD3, 0xE0, 0x89, 0xC1, 0xD1, 0xE0, 0x48, 0x48, 0x8B, 0xF0, 0x8B, 0xF8, 0xF3, 0xA5, 0x09, 0xED, 0x75, 0xD8, 0xFC, 0x8E, 0xC2, 0x8E, 0xDB, 0x31, 0xF6, 0x31, 0xFF, 0xBA, 0x10, 0x00, 0xAD, 0x89, 0xC5, 0xD1, 0xED, 0x4A, 0x75, 0x05, 0xAD, 0x89, 0xC5, 0xB2, 0x10, 0x73, 0x03, 0xA4, 0xEB, 0xF1, 0x31, 0xC9, 0xD1, 0xED, 0x4A, 0x75, 0x05, 0xAD, 0x89, 0xC5, 0xB2, 0x10, 0x72, 0x22, 0xD1, 0xED, 0x4A, 0x75, 0x05, 0xAD, 0x89, 0xC5, 0xB2, 0x10, 0xD1, 0xD1, 0xD1, 0xED, 0x4A, 0x75, 0x05, 0xAD, 0x89, 0xC5, 0xB2, 0x10, 0xD1, 0xD1, 0x41, 0x41, 0xAC, 0xB7, 0xFF, 0x8A, 0xD8, 0xE9, 0x13, 0x00, 0xAD, 0x8B, 0xD8, 0xB1, 0x03, 0xD2, 0xEF, 0x80, 0xCF, 0xE0, 0x80, 0xE4, 0x07, 0x74, 0x0C, 0x88, 0xE1, 0x41, 0x41, 0x26, 0x8A, 0x01, 0xAA, 0xE2, 0xFA, 0xEB, 0xA6, 0xAC, 0x08, 0xC0, 0x74, 0x34, 0x3C, 0x01, 0x74, 0x05, 0x88, 0xC1, 0x41, 0xEB, 0xEA, 0x89, 0xFB, 0x83, 0xE7, 0x0F, 0x81, 0xC7, 0x00, 0x20, 0xB1, 0x04, 0xD3, 0xEB, 0x8C, 0xC0, 0x01, 0xD8, 0x2D, 0x00, 0x02, 0x8E, 0xC0, 0x89, 0xF3, 0x83, 0xE6, 0x0F, 0xD3, 0xEB, 0x8C, 0xD8, 0x01, 0xD8, 0x8E, 0xD8, 0xE9, 0x72 }, sigbuf [sizeof sig90]; /* EXE header test (is it LZEXE file?) */ int rdhead (FILE *ifile ,int *ver) { long entry; /* v0.8 */ /* v0.7 old code */ /* if(fread(ihead,sizeof ihead[0],0x10,ifile)!=0x10) * return FAILURE; * memcpy(ohead,ihead,sizeof ihead[0] * 0x10); * if(ihead[0]!=0x5a4d || ihead[4]!=2 || ihead[0x0d]!=0) * return FAILURE; * if(ihead[0x0c]==0x1c && memcmp(&ihead[0x0e],"LZ09",4)==0){ * *ver=90; return SUCCESS ; * } * if(ihead[0x0c]==0x1c && memcmp(&ihead[0x0e],"LZ91",4)==0){ * *ver=91; return SUCCESS ; * } */ /* v0.8 */ if (fread (ihead, 1, sizeof ihead, ifile) != sizeof ihead) return FAILURE; memcpy (ohead, ihead, sizeof ohead); if((ihead [0] != 0x5a4d && ihead [0] != 0x4d5a) || ihead [0x0d] != 0 || ihead [0x0c] != 0x1c) return FAILURE; entry = ((long) (ihead [4] + ihead[0x0b]) << 4) + ihead[0x0a]; if (fseek (ifile, entry, SEEK_SET) != 0) return FAILURE; if (fread (sigbuf, 1, sizeof sigbuf, ifile) != sizeof sigbuf) return FAILURE; if (memcmp (sigbuf, sig90, sizeof sigbuf) == 0) { *ver = 90; return SUCCESS; } if (memcmp (sigbuf, sig91, sizeof sigbuf) == 0) { *ver = 91; return SUCCESS; } return FAILURE; } /* make relocation table */ int mkreltbl (FILE *ifile, FILE *ofile, int ver) { int reloc90(); int reloc91(); long fpos; int i; /* v0.7 old code * allocsize=((ihead[1]+16-1)>>4) + ((ihead[2]-1)<<5) - ihead[4] + ihead[5]; */ fpos = (long)(ihead[0x0b]+ihead[4])<<4; /* goto CS:0000 */ fseek (ifile, fpos, SEEK_SET); fread (inf, sizeof inf[0], 0x08, ifile); ohead[0x0a]=inf[0]; /* IP */ ohead[0x0b]=inf[1]; /* CS */ ohead[0x08]=inf[2]; /* SP */ ohead[0x07]=inf[3]; /* SS */ /* inf[4]:size of compressed load module (PARAGRAPH)*/ /* inf[5]:increase of load module size (PARAGRAPH)*/ /* inf[6]:size of decompressor with compressed relocation table (BYTE) */ /* inf[7]:check sum of decompresser with compressd relocation table(Ver.0.90) */ ohead[0x0c]=0x1c; /* start position of relocation table */ fseek (ofile, 0x1cL, SEEK_SET); switch (ver) { case 90: i=reloc90 (ifile,ofile,fpos); break; case 91: i=reloc91 (ifile,ofile,fpos); break; default: printf ("bad version 0.%d\n", ver); i=FAILURE; break; } if (i!=SUCCESS) { printf ("Error at relocation table\n"); return (FAILURE); } fpos = ftell (ofile); /* v0.7 old code * i= (int) fpos & 0x1ff; * if(i) i=0x200-i; * ohead[4]= (int) (fpos+i)>>4; */ i= (0x200 - (int) fpos) & 0x1ff; ohead[4]= (int) ((fpos+i)>>4); for( ; i>0; i--) putc(0, ofile); return(SUCCESS); } /* for LZEXE ver 0.90 */ int reloc90 (FILE *ifile, FILE *ofile, long fpos) { unsigned int c; WORD rel_count=0; WORD rel_seg,rel_off; /* 0x19d=compressed relocation table address */ fseek(ifile,fpos+0x19d,SEEK_SET); rel_seg = 0; do { if (feof(ifile) || ferror(ifile) || ferror(ofile)) return(FAILURE); c = getc(ifile) + getc(ifile)*256; for(;c>0;c--) { rel_off = getc(ifile) + getc(ifile)*256; putc (rel_off & 255, ofile); putc (rel_off / 256, ofile); putc (rel_seg & 255, ofile); putc (rel_seg / 256, ofile); rel_count++; } rel_seg += 0x1000; } while (rel_seg!=(0xf000+0x1000)); ohead[3]=rel_count; return(SUCCESS); } /* for LZEXE ver 0.91*/ int reloc91 (FILE *ifile, FILE *ofile, long fpos) { int span; int rel_count=0; int rel_seg,rel_off; /* 0x158=compressed relocation table address */ fseek (ifile, fpos+0x158, SEEK_SET); rel_off=0; rel_seg=0; for(;;) { if (feof(ifile) || ferror(ifile) || ferror(ofile)) return(FAILURE); if ((span=getc(ifile))==0) { span = getc(ifile) + getc(ifile)*256; if(span==0) { rel_seg += 0x0fff; continue; } else if(span==1) break; } rel_off += span; rel_seg += (rel_off & ~0x0f)>>4; rel_off &= 0x0f; putc (rel_off & 255, ofile); putc (rel_off / 256, ofile); putc (rel_seg & 255, ofile); putc (rel_seg / 256, ofile); rel_count++; } ohead[3] = rel_count; return (SUCCESS); } /*---------------------*/ typedef struct { FILE *fp; WORD buf; BYTE count; } bitstream; void initbits (bitstream *,FILE *); int getbit (bitstream *); /*---------------------*/ /* decompressor routine */ int unpack (FILE *ifile, FILE *ofile) { int len; int span; long fpos; bitstream bits; static BYTE data[0x4500], *p=data; fpos = ((long)ihead[0x0b]-(long)inf[4]+(long)ihead[4])<<4; fseek (ifile, fpos, SEEK_SET); fpos = (long)ohead[4]<<4; fseek (ofile, fpos, SEEK_SET); initbits (&bits, ifile); printf ("progress: "); for(;;) { if (ferror(ifile)) { perror ("\nRead error\n"); return (FAILURE); } if (ferror(ofile)) { perror ("\nWrite error\n"); return (FAILURE); } if (p-data>0x4000) { fwrite (data,sizeof data[0],0x2000,ofile); p -= 0x2000; memcpy (data,data+0x2000,p-data); putchar ('.'); } if (getbit(&bits)) { *p++ = getc(ifile); continue; } if (!getbit(&bits)) { len = getbit(&bits)<<1; len |= getbit(&bits); len += 2; span = getc(ifile) | 0xffffff00; } else { span = getc (ifile); len = getc (ifile); span |= ((len & ~0x07)<<5) | 0xffffe000; len = (len & 0x07)+2; if (len==2) { len = getc (ifile); if (len==0) break; /* end mark of compreesed load module */ if (len==1) continue; /* segment change */ else len++; } } for( ;len>0;len--,p++) { *p=*(p+span); } } if (p!=data) fwrite (data, sizeof data[0], p-data, ofile); loadsize = ftell(ofile)-fpos; printf (".\n"); return (SUCCESS); } /* write EXE header */ void wrhead (FILE *ofile) { if (ihead[6]!=0) { ohead[5]-= inf[5] + ((inf[6]+16-1)>>4) + 9; /* v0.7 */ if(ihead[6]!=0xffff) ohead[6]-=(ihead[5]-ohead[5]); } ohead[1]=((WORD)loadsize+(ohead[4]<<4)) & 0x1ff; /* v0.7 */ ohead[2]=(WORD)((loadsize+((long)ohead[4]<<4)+0x1ff) >> 9); /* v0.7 */ fseek (ofile, 0L, SEEK_SET); fwrite (ohead, sizeof ohead[0], 0x0e, ofile); } /*-------------------------------------------*/ /* get compress information bit by bit */ void initbits (bitstream *p, FILE *filep) { p->fp=filep; p->count=0x10; p->buf = getc(filep) + getc(filep)*256; /* printf("%04x ",p->buf); */ } int getbit (bitstream *p) { int b; b = p->buf & 1; if(--p->count == 0) { (p->buf) = getc(p->fp) + getc(p->fp)*256; /* printf("%04x ",p->buf); */ p->count= 0x10; } else p->buf >>= 1; return b; }