TIP: Click on subject to list as thread! ANSI
echo: c_echo
to: Bill Birrell
from: Tom Torfs
date: 1998-10-13 23:27:48
subject: exe files (was: music)

Bill Birrell wrote in a message to Corbin Lemieux:

 BB>     Corbin, the best way to see what's in the header of an EXE file
 BB> on Ms-Dos is to run exehdr.exe on it. This comes free with most of
 BB> the Microsoft programming languages, so you probably have a copy
 BB> somewhere.

This may also be of help:

/*
   exinfo.c - displays information about executables
   written by Tom Torfs (tomtorfs{at}mail.dma.be, 2:292/516{at}fidonet.org)
   donated to the public domain, 1996-12-16

   if you update this program, please send me a copy

   identifies the following executable formats:
      * DOS COM, DOS EXE, European MS-DOS 4.x new executable
      * Windows segmented, linear
      * Windows/386 segmented, linear
      * Win32 (Win32s,Win95,WinNT)
      * OS/2 segmented, linear, family app
      * Borland OS services, HP 100LX/200LX
      * PharLap, PharLap/286, PharLap/386, PharLap/OS/2, PharLap/Windows

   identifies the following compilers/linkers: (these can only be
   detected on uncompressed executables)
      * Borland/Turbo C/C++
      * Watcom C/C++
      * TopSpeed C/C++

   identifies the following executable compressors / sfx archives:
      * PKZIP sfx, PKARC sfx
      * ARJ sfx, RAR SFX, BSA sfx
      * LHarc SFX, LHA SFX, LH SFX, LARC SFX
      * LZEXE, PKLITE, DIET, TINYPROG
*/

#include 
#include 
#include 

/* turn alignment off */
#if defined(__TURBOC__)
#pragma option -a-
#elif defined(__ZTC__)
#pragma ZTC align 1
#else
#pragma pack(1)
#endif

/* executable formats */
enum {FORM_UNKNOWN,
      FORM_DOSCOM,FORM_DOSEXE,
      FORM_DOS4SEGMENTED,FORM_DOS4LINEAR,
      FORM_SEGMENTED,FORM_LINEAR,
      FORM_WINSEGMENTED,FORM_WINLINEAR,
      FORM_WIN386SEGMENTED,FORM_WIN386LINEAR,
      FORM_WIN32,
      FORM_OS2SEGMENTED,FORM_OS2LINEAR,FORM_OS2FAMILY,
      FORM_BORLAND,FORM_HPLX,
      FORM_PHARLAP,FORM_PHARLAP286,FORM_PHARLAP386,
      FORM_PHARLAPOS2,FORM_PHARLAPWIN,
      FORMATS};

/* compilers/linkers */
enum {COMP_UNKNOWN,
      COMP_BORLANDC,COMP_WATCOMC,COMP_TOPSPEEDC,
      COMP_TLINK,
      COMPILERS};

/* executable packers */
enum {PACK_UNKNOWN,
      PACK_PKZIPSFX,PACK_PKARCSFX,PACK_ARJSFX,PACK_RARSFX,
      PACK_BSASFX,PACK_LHARCSFX,PACK_LHASFX,PACK_LHSFX,PACK_LARCSFX,
      PACK_LZEXE,PACK_PKLITE,PACK_DIET,PACK_TINYPROG,
      PACKERS};

const char *formatname[FORMATS]
   = {"unknown",
      "DOS COM","DOS EXE",
      "European MS-DOS 4.x segmented","European MS-DOS 4.x
linear",
      "segmented executable","linear executable",
      "Windows segmented","Windows linear",
      "Win386 segmented","Win386 linear",
      "Win32",
      "OS/2 segmented","OS/2 linear","OS/2 family app",
      "Borland OS services","HP 100LX/200LX",
      "PharLap","PharLap/286","PharLap/386",
      "PharLap/OS/2","Pharlap/Windows"};

const char *compilername[COMPILERS]
   = {"unknown",
      "Borland/Turbo C/C++","Watcom
C/C++","TopSpeed C/C++",
      "Borland TLINK"};

const char *packername[PACKERS]
   = {"unknown/none",
      "PKZIP SFX","PKARC SFX","ARJ
SFX","RAR SFX",
      "BSA SFX","LHarc SFX","LHA
SFX","LH SFX","LARC SFX",
      "LZEXE","PKLITE","DIET","TINYPROG"};

#define MAXPATTERNLEN 20

struct pattern {int compiler;
                int patternlen; unsigned char pattern[MAXPATTERNLEN];};

#define PATTERNS 3

/* compiler patterns
   0 is a wildcard (any value will match)
   as soon as a match is found, the detection routine will abort,
   so specific matches should come before more general ones */
static struct pattern patterns[PATTERNS]
   = {
      { COMP_BORLANDC,  12,         /* Borland C/C++ exe */
        {0xBA,0,0,0x2E,0x89,0x16,0,0,0xB4,0x30,0xCD,0x21} },
      { COMP_BORLANDC,  12,         /* Borland C/C++ com */
        {0x8C,0xCA,0x2E,0x89,0x16,0,0,0xB4,0x30,0xCD,0x21} },
      { COMP_WATCOMC,   9,          /* Watcom C/C++ exe/com */
        {0xE9,0,0,'W','A','T','C','O','M'} }
     };

static struct {char signature[2];
               unsigned short lenmod512,lendiv512;
               unsigned short fixups;
               unsigned short headerpara,memminpara,memmaxpara;
               unsigned short ss,sp;
               unsigned short checksum;
               unsigned short ip,cs;
               unsigned short fixuptabofs;
               unsigned short overlay;
     } exeheader;

static struct {char signature[2];
               unsigned char linkermajor,linkerminor;
               unsigned short entryofs,entrylen;
               unsigned long crc;
               unsigned char progflags,appflags;
               unsigned short datasegidx,localheapsize,stacksize;
               unsigned short ip,cs;
               unsigned short sp,ss;
               unsigned short segcount,refcount,nonresnametablen;
               unsigned short segtabofs,resourcetabofs,resnametabofs,
                              reftabofs,impnametabofs;
               unsigned long nonresnametabofs;
               unsigned short entrypointcount;
               unsigned short alignment;
               unsigned short resourcecount;
               unsigned char targetos;
               unsigned char exeflags;
               unsigned short gangloadofs,gangloadlength;
               unsigned short swapsize;
               unsigned char winminor,winmajor;               
       } newheader;

static struct {char signature[2];
               unsigned char byteorder,wordorder;
               unsigned long formatlevel;
               unsigned short cpu;
               unsigned short targetos;
               unsigned long modversion,modtype;
               unsigned long mempages;
               unsigned long csipobjnr,csipofs;
               unsigned long ssspobjnr,ssspofs;
               unsigned long mempagesize;
               unsigned long lastpage;
               unsigned long fixupsize,fixupchecksum;
               unsigned long loadersize,loaderchecksum;
               unsigned long objtabofs,objtabcount;
               unsigned long objpagetabofs,objdatamapofs;
               unsigned long resourcetabofs,resourcetabsize;
               unsigned long resnametabofs,entrytabofs;
               unsigned long directivetabofs,directivecount;
               unsigned long fixuppagetabofs,fixuprecordtabofs;
               unsigned long nametabofs,impcount,impnametabofs;
               unsigned long checksumtabofs,datapageofs;
               unsigned long preloadcount;
               unsigned long nonresnametabofs,nonresnametabsize;
               unsigned long nonresnametabchecksum;
               unsigned long autodata;
               unsigned long debugoffset,debuglength;
               unsigned long preloadnumber,demandnumber;
               unsigned long extraheap;
       } linearheader;

static unsigned char buffer[MAXPATTERNLEN];

/* compare memory with wildcards */
static int maskcomp(const unsigned char *mem,
                    const unsigned char *mask,
                    int len)
{
   for (; len>0; len--, mem++, mask++)
   {
      if (*mask!=0 && *mask!=*mem)
         return 1;
   }
   return 0;
}

/* read a string from a file terminated by character term
   s must be able to hold maxlen characters + the nul terminator
   returns number of characters actually read */
static int freadstr(FILE *fp, unsigned char *s, int maxlen, unsigned char term)
{
   int len = 0;
   
   while (len=0x40
          || (exeheader.fixups==0 && exeheader.headerpara>=4))
      {
    fseek(fp,0x3C,SEEK_SET);
    fread(&newheaderpos,1,sizeof(long),fp);
    if (newheaderpos!=0)
    {
       /* check for new executable header */
       fseek(fp,newheaderpos,SEEK_SET);
       if (fread(&newheader,1,2,fp) == 2)
       {
          newexe = 1;
          if (memcmp(newheader.signature,"NE",2) == 0)
          {
             segexe = 1;
             /* read rest of new header */
             fread(&newheader.linkermajor,1,sizeof newheader-2,fp);
             switch(newheader.targetos)
             {
             case 0x01:
                if (newheader.appflags&8
                    || overlaypos==fsize)
                   format = FORM_OS2FAMILY;
                else
                       format = FORM_OS2SEGMENTED;
                break;
             case 0x02:
                format = FORM_WINSEGMENTED;
                break;
             case 0x03:
                format = FORM_DOS4SEGMENTED;
                break;
             case 0x04:
                format = FORM_WIN386SEGMENTED;
                break;
             case 0x05:
                format = FORM_BORLAND;
                break;
             case 0x81:
                format = FORM_PHARLAPOS2;
                break;
             case 0x82:
                format = FORM_PHARLAPWIN;
                break;
             default:
                format = FORM_SEGMENTED;
                break;
             }
               }
          else if (memcmp(newheader.signature,"LE",2) == 0
                     || memcmp(newheader.signature,"LX",2) == 0)
          {
             linearexe = 1;
             memcpy(linearheader.signature,newheader.signature,2);
             /* read rest of linear header */
             fread(&linearheader.byteorder,1,sizeof linearheader-2,fp);
             switch(linearheader.targetos)
             {
             case 0x01:
                if (linearheader.signature[1]=='X')
                      format = FORM_OS2LINEAR;
                   else
                      format = FORM_LINEAR;
                break;
             case 0x02:
                format = FORM_WINLINEAR;
                break;
             case 0x03:
                format = FORM_DOS4LINEAR;
                break;
             case 0x04:
                format = FORM_WIN386LINEAR;
                break;
             default:
                format = FORM_LINEAR;
                break;
             }
          }
          else if (memcmp(newheader.signature,"W3",2) == 0)
             format = FORM_WIN386LINEAR;
          else if (memcmp(newheader.signature,"PE",2) == 0)
             format = FORM_WIN32;
          else if (memcmp(newheader.signature,"DL",2) == 0)
             format = FORM_HPLX;
          else if (memcmp(newheader.signature,"MP",2) == 0)
             format = FORM_PHARLAP;
          else if (memcmp(newheader.signature,"P2",2) == 0)
             format = FORM_PHARLAP286;
          else if (memcmp(newheader.signature,"P3",2) == 0)
             format = FORM_PHARLAP386;
       }
    }
      }
      /* calculate offset of initial cs:ip in file */
      startpos = (long) exeheader.headerpara * 16 
               + (long) exeheader.cs * 16
               +        exeheader.ip;
   }
   else
   {
      /* this may be a dos com... */
      if (fsize<65280)
      {
         /* ...but only if it's smaller than 64K - 256 bytes */
         format = FORM_DOSCOM;
         startpos = 0;
      }
   }

   if (format != FORM_UNKNOWN)
   {
      /* compare against the different compiler patterns */
      fseek(fp,startpos,SEEK_SET);
      fread(buffer,1,MAXPATTERNLEN,fp);
      for (i=0; i0x22)
   {
      /* check for TLINK */
      fseek(fp,0x1C,SEEK_SET);
      fread(buffer,1,6,fp);
      if (buffer[0]==0x01 && buffer[1]==0x00 && buffer[2]==0xFB)
         compiler = COMP_TLINK;
      /* check for TopSpeed C CRUNCH */
      else if (buffer[0]==0x01 && buffer[1]==0x00 && buffer[2]==0x8A
               && buffer[3]==0x01 && buffer[4]==0x65
&& buffer[5]==0x15)
         compiler = COMP_TOPSPEEDC;
   }

   if (format == FORM_DOSCOM)
   {
      if (packer == PACK_UNKNOWN
          && fsize>0x2E)
      {
         /* check for PKLITE */
         fseek(fp,0x2E,SEEK_SET);
         fread(buffer,1,6,fp);
         if (memcmp(buffer,"PKLITE",6)==0)
            packer = PACK_PKLITE;
         fseek(fp,0x2E,SEEK_SET);
         freadstr(fp,copyright,127,0);
         cp = strstr(copyright,"Reserved");
         if (cp!=NULL)
            cp[8] = '\0';
      }
      if (packer == PACK_UNKNOWN
          && fsize>0x25)
      {
         /* check for DIET */
         fseek(fp,0x23,SEEK_SET);
         fread(buffer,1,3,fp);
         if (memcmp(buffer,"dlz",3)==0)
            packer = PACK_DIET;
      }
   }
   else
   {
      if (packer == PACK_UNKNOWN
          && fsize>=overlaypos+5)
      {
         /* check for PKZIP SFX */
         fseek(fp,overlaypos,SEEK_SET);
         fread(buffer,1,5,fp);
         if (memcmp(buffer,"PK",2)==0 && buffer[2]==0x03
&& buffer[3]==0x04)
            packer = PACK_PKZIPSFX;
      }
      if (packer == PACK_UNKNOWN
          && fsize>0x22)
      {
         /* check for PKARC SFX */
         fseek(fp,0x1C,SEEK_SET);
         fread(buffer,1,6,fp);
         if (buffer[0]==0x01 && buffer[1]==0x00 && buffer[2]==0x02
             && buffer[3]==0x00 && buffer[4]==0x00
&& buffer[5]==0x07)
            packer = PACK_PKARCSFX;
      }
      if (packer == PACK_UNKNOWN
          && fsize>0x20)
      {
         /* check for ARJ SFX */
         fseek(fp,0x1C,SEEK_SET);
         fread(buffer,1,4,fp);
         if (memcmp(buffer,"RJSX",4)==0)
            packer = PACK_ARJSFX;
      }
      if (packer == PACK_UNKNOWN
          & fsize>0x20)
      {
         /* check for RAR SFX */
         fseek(fp,0x1C,SEEK_SET);
         fread(buffer,1,4,fp);
         if (memcmp(buffer,"RSFX",4)==0)
            packer = PACK_RARSFX;
      }
      if (packer == PACK_UNKNOWN
          && fsize>0x1F)
      {
         /* check for BSA SFX */
         fseek(fp,0x1C,SEEK_SET);
         fread(buffer,1,3,fp);
         if (buffer[0]==0x0F && buffer[1]==0x00 && buffer[2]==0xA7)
            packer = PACK_BSASFX;
      }
      if (packer == PACK_UNKNOWN
          && fsize>0x30)
      {
         /* check for LHarc SFX */
         fseek(fp,0x25,SEEK_SET);
         fread(buffer,1,11,fp);
         if (memcmp(buffer,"LHarc's SFX",11)==0)
            packer = PACK_LHARCSFX;
      }
      if (packer == PACK_UNKNOWN
          && fsize>0x2D)
      {
         /* check for LHA SFX */
         fseek(fp,0x24,SEEK_SET);
         fread(buffer,1,9,fp);
         if (memicmp(buffer,"LHA's SFX",9)==0) /* ignore case (LHa) */
            packer = PACK_LHASFX;
      }
      if (packer == PACK_UNKNOWN
          && fsize>0x2C)
      {
         /* check for LH SFX */
         fseek(fp,0x24,SEEK_SET);
         fread(buffer,1,9,fp);
         if (memcmp(buffer,"LH's SFX",8)==0)
            packer = PACK_LHSFX;
      }
      if (packer == PACK_UNKNOWN
          && fsize>0x2B)
      {
         /* check for LARC SFX */
         fseek(fp,0x20,SEEK_SET);
         fread(buffer,1,11,fp);
         if (memcmp(buffer,"SFX by LARC",11)==0)
            packer = PACK_LARCSFX;
      }
      if (packer == PACK_UNKNOWN
          && exeheader.fixups==0)
      {
         /* check for LZEXE */
         fseek(fp,0x1C,SEEK_SET);
         fread(buffer,1,2,fp);
         if (memcmp(buffer,"LZ",2)==0)
            packer = PACK_LZEXE;
      }
      if (packer == PACK_UNKNOWN
          && (exeheader.fixuptabofs>=0x24
              || (exeheader.fixups==0 && exeheader.headerpara>=3)))
      {
         /* check for PKLITE */
         fseek(fp,0x1C,SEEK_SET);
         fread(buffer,1,8,fp);
         if (memcmp(buffer+2,"PKLITE",6)==0)
            packer = PACK_PKLITE;
         fseek(fp,0x1E,SEEK_SET);
         freadstr(fp,copyright,127,0);
         cp = strstr(copyright,"Reserved");
         if (cp!=NULL)
            cp[8] = '\0';
      }
      if (packer == PACK_UNKNOWN
          && fsize>0x59)
      {
         /* check for DIET */
         fseek(fp,0x57,SEEK_SET);
         fread(buffer,1,3,fp);
         if (memcmp(buffer,"dlz",3)==0)
            packer = PACK_DIET;
      }
      if (packer == PACK_UNKNOWN
          && fsize>0x1E)
      {
         /* check for TINYPROG */
         fseek(fp,0x1C,SEEK_SET);
         fread(buffer,1,2,fp);
         if (memcmp(buffer,"tz",2)==0)
            packer = PACK_TINYPROG;
      }
   }

   /* now look for the compiler's copyright string */
   switch(compiler)
   {
   case COMP_BORLANDC:
      switch(format)
      {
      case FORM_DOSCOM:
         fseek(fp,0x1C,SEEK_SET);
         fread(buffer,1,4,fp);
         if (buffer[0]==0x89 && buffer[1]==0x1E)
         {
            copyrightpos = *(unsigned short *)(buffer+2) - 0x16F;
            fseek(fp,copyrightpos,SEEK_SET);
            freadstr(fp,copyright,127,0);
         }
         break;
      case FORM_DOSEXE:
         fseek(fp,startpos+1,SEEK_SET);
         fread(buffer,1,2,fp);
         copyrightpos = (long) exeheader.headerpara * 16
                      + (long) *(unsigned short *)buffer * 16
                      + 4;
         fseek(fp,copyrightpos,SEEK_SET);
         freadstr(fp,copyright,127,0);
         break;
      }
      if (memicmp(copyright,"Borland",7) != 0
          && memicmp(copyright,"Turbo",5) != 0)
         copyright[0] = '\0';
      break;
   case COMP_WATCOMC:
      fseek(fp,startpos+3,SEEK_SET);
      freadstr(fp,copyright,127,13);
      if (memicmp(copyright,"WATCOM",6) != 0)
         copyright[0] = '\0';
      else
      {
         cp = strstr(copyright,"reserved.");
         if (cp!=NULL)
            cp[9] = '\0';
      }         
      break;
   }

   printf("filename   : %s\n",argv[1]);
   printf("format     : %s\n",formatname[format]);
   if (linearexe)
   {
      printf("type       : ");
      switch((linearheader.modtype>>15)&3)
      {
      case 0:
         if (((linearheader.modtype>>8)&7)==3)
            printf("GUI program\n");
         else
            printf("console program\n");
            break;
      case 1:
         printf("library (DLL)\n");
         break;
      case 3:
         printf("protected mode library module\n");
         break;
      case 4:
         printf("physical device driver\n");
         break;
      case 6:
         printf("virtual device driver\n");
         break;
      default:
         printf("unknown\n");
         break;
      }
      printf("CPU type   : 80%d86\n",linearheader.cpu + 1);
   }
   else if (segexe)
   {
      printf("type       : ");
      if (newheader.appflags&0x80)
         printf("DLL/driver\n");
      else if ((newheader.appflags&7)==3)
         printf("GUI program\n");
      else
         printf("console program\n");
      printf("CPU type   : 80");
      if (newheader.progflags&64)
         printf("386\n");
      else if (newheader.progflags&32)
         printf("286\n");
      else
         printf("86\n");
   }
   printf("file size  : %ld bytes\n",fsize);
   if (format==FORM_DOSEXE)
   {
      printf("code size  : %ld
bytes\n",overlaypos-(long)exeheader.headerpara*16);
      printf("overlays   : %ld bytes\n",fsize-overlaypos);
      printf("stack size : %u bytes\n",exeheader.sp);
      printf("memory     : %ld - %ld bytes\n", 
                  (long)exeheader.memminpara*16,(long)exeheader.memmaxpara*16);
      printf("fixups     : %u\n",exeheader.fixups);
   }
   else if (format!=FORM_DOSCOM)
   {
      printf("DOS code   : %ld
bytes\n",overlaypos-(long)exeheader.headerpara*16);
      printf("DOS stack  : %u bytes\n",exeheader.sp);
      printf("DOS memory : %ld - %ld bytes\n", 
                  (long)exeheader.memminpara*16,(long)exeheader.memmaxpara*16);
      printf("DOS fixups : %u\n",exeheader.fixups);
   }
   printf("compiler   : %s\n",compilername[compiler]);
   printf("packer     : %s\n",packername[packer]);
   if (copyright[0]!='\0')
      printf("copyright  : %s\n",copyright);

   return EXIT_SUCCESS;
}

greetings,
Tom
tomtorfs{at}village.uunet.be

--- timEd/2 1.10+
* Origin: 80X86 BBS 32-15-24.62.32 V.34/V.FC (24h/24h) (2:292/516)
SEEN-BY: 396/1 632/0 371 633/260 267 270 371 634/397 635/506 728 810 639/252
SEEN-BY: 670/218
@PATH: 292/516 106/1 396/1 633/260 635/506 728 633/267

SOURCE: echomail via fidonet.ozzmosis.com

Email questions or comments to sysop@ipingthereforeiam.com
All parts of this website painstakingly hand-crafted in the U.S.A.!
IPTIA BBS/MUD/Terminal/Game Server List, © 2025 IPTIA Consulting™.