[Back to DOS SWAG index]  [Back to Main SWAG index]  [Original]

{
Hi !

   Here is some source code I acquired from a Pascal echo some time
   ago. It shows one method of detecting which TP compiler created
   an .EXE:

-------------------------------------------------------------------
{ to compile type: tpc foo.pas }
{ exe: 9776 bytes by TP5.5 }

{$A+,B-,E-,F-,I+,N-,O-,V+}
{$M 4500,0,0}
{$ifndef debug}
{$D-,L-,R-,S-}
{$else}
{$D+,L+,R+,S+}
{$endif}

Program foo;

Uses
   DOS;  { dos unit from turbo pascal }

TYPE              { normal exe file header }
    EXEH = RECORD
          id,            { exe signature }
          Lpage,         { exe file size mod 512 bytes; < 512 bytes }
          Fpages,        { exe file size div 512 bytes; + 1 if Lpage > 0 }
          relocitems,    { number of relocation table items }
          size,          { exe header size in 16-byte paragraphs }
          minalloc,      { min mem. required in additional to exe image }
          maxalloc,      { extra max. mem. desired beyond that required
                           to hold exe's image }
          ss,            { displacement of stack segment }
          sp,            { initial SP register value }
          chk_sum,       { complemented checksum }
          ip,            { initial IP register value }
          cs,            { displacement of code segment }
          ofs_rtbl,      { offset to first relocation item }
          ovr_num : word; { overlay numbers }
       END;
                { window exe file header }
    WINH = RECORD
          id : word;     { ignore the rest of data structures }
       END;

    str2  = string [2];
    str4  = string [4];
    str10 = string [10];

CONST
    no_error  = 0;        { no system error }
    t         = #9;       { ascii: hortizon tab }
    dt        = t+t;
    tt        = t+t+t;
    qt        = t+t+t+t;
    cr        = #13#10;   { ascii: carriage return and line feed }

VAR
    f        : file;      { source file, untyped }
    exehdr   : exeh;      { exe header contents }
    winhdr   : winh;      { window exe header contents }
    blocks_r : word;      { number of blocks actually read }

    exe_size ,            { exe file length }
    hdr_size ,            { exe header size }
    img_size ,            { load module or exe image size }
    min_xmem ,            { min. extra memory needed }
    max_xmem ,            { max. extra memory wanted }
    o_starup : longint;   { offset to start up code }

    dirfile    : searchrec;
    compressed : boolean;

function Hex(B :byte) :str2;
 CONST  strdex :array [0..$F] of char = '0123456789ABCDEF';
 BEGIN  Hex := concat(strdex[B shr 4], strdex[B and $F]); END;

function HexW(W :word) :str4;
 VAR    byt :array [0..1] of byte absolute W;
 BEGIN  HexW := Hex(byt[1])+Hex(byt[0]); END;

function HexL(L :longint) :str10;
 TYPE   Cast = RECORD
                Lo :word;
                Hi :word;
         END;
 BEGIN  HexL := HexW(Cast(L).Hi)+' '+HexW(Cast(L).Lo); END;

procedure print_info;
   CONST
      psp_size = $100; { size of psp, bytes }
   VAR   i : byte;
   BEGIN
      hdr_size := longint(exehdr.size) shl 4;       { exe header size, bytes }
      img_size := longint(exe_size) - hdr_size;     { exe image size, bytes }
      min_xmem := longint(exehdr.minalloc) shl 4;   { mim xtra mem, bytes }
      max_xmem := longint(exehdr.maxalloc) shl 4;   { max xtra mem, bytes }
      o_starup := hdr_size + longint(exehdr.cs) shl 4
                  +longint(exehdr.ip);              { ofs to start up code  }
      writeln(
         qt, 'Dec':8, '':6, 'Hex', cr,
         'EXE file size:', tt, exe_size:8, '':3, hexl(exe_size), cr,
         'EXE header size:', dt, hdr_size:8, '':3, hexl(hdr_size), cr,
         'Code + initialized data size:', t, img_size:8, '':3, hexl(img_size)
             );

      writeln(
         'Pre-relocated SS:SP', tt, '':3, hexw(exehdr.ss), ':', hexw(exehdr.sp)
         , cr,
         'Pre-relocated CS:IP', tt, '':3, hexw(exehdr.cs), ':', hexw(exehdr.ip)
             );

      writeln(
         'Min. extra memory required:', t, min_xmem:8, '':3, hexl(min_xmem), cr,
         'Max. extra memory wanted:', t, max_xmem:8, '':3, hexl(max_xmem), cr,
         'Offset to start up code:', dt, '':3, hexl(o_starup), cr,
         'Offset to relocation table:', dt, '':3, hexw(exehdr.ofs_rtbl):9
             );

     writeln(
         'Number of relocation pointers:', t, exehdr.relocitems:8, cr,
         'Number of MS overlays:', dt, exehdr.ovr_num:8, cr,
         'File checksum value:', tt, '':3, hexw(exehdr.chk_sum):9, cr,
         'Memory needed to start:', dt, img_size+min_xmem+psp_size:8
            );
END; { print_info }

procedure id_signature;    { the core of this program }
   CONST
      o_01    =  14;        { relative offset from cstr0 to cstr1 }
      o_02    =  16;        {   "        "      "  cstr0 to cstr2 }
      o_03    =  47;        {   "        "      "  cstr0 to cstr3 }
      cstr0   = 'ntime';    { constant string existed in v4-6 }
      cstr1   = 'at '#0'.'; { constant string existed in v4-6 }
      cstr2   = '$4567';    { constant string existed in v5-6 }
      cstr3   = '83,90';    { constant string existed in v6 only }
      strlen  =   5;        { length of cstr? }
      ar_itm  =   3;        { items+1 of string array }

   { the following figures have been turn-up explicitly and
     should not be changed }

      ofs_rte =  25 shl 4;  { get close to 'run time error' str contants }
      maxchar =  11 shl 4;  { max. size of buffer; for scanning }

   TYPE
      arstr  = array [0..ar_itm] of string[strlen];
      arbuf  = array [0..maxchar] of char;

   VAR
      i, j, k : word;    { index counter for array buffer }
      cstr    : arstr;   { signatures generated by tp compiler }
      o_fseg  : word;    { to hold segment value of any far call }
      o_sysseg: longint; { offset to tp system_unit_segment }
      buffer  : arbuf;   { searching for target strings }

   BEGIN
{d}   Seek(f, o_starup + 3);                       { move file pointer 
forward 3 bytes }
{d}   BlockRead(f, o_fseg, sizeof(o_fseg));        { get far call segment 
value }
      o_sysseg := longint(o_fseg) shl 4 +hdr_size; { ofs to system obj code }
      if (o_sysseg + ofs_rte <= dirfile.size) then
      BEGIN
{d}      Seek(f, o_sysseg+ofs_rte);                { offset nearby tp 
signatures }
{d}      BlockRead(f, buffer, sizeof(buffer), blocks_r);
         for i := 0 to ar_itm do
         BEGIN
             cstr[i][0] := char(strlen);
             fillchar(cstr[i][1], strlen, '*');
         END;
         i := 1; j := 1; k := 0;
         repeat
            if buffer[i] in ['n','t','i','m','e'] then
            BEGIN
               if (k > 0) and (k = i - 1) then
                  inc(j);
               cstr[0][j] := buffer[i];
               k := i;
            END;
            inc(i);
         until (cstr[0] = cstr0) or (i > maxchar) or (j > strlen);
         if (i+o_03 <= maxchar) then
         BEGIN
            dec(i, strlen);
            move(buffer[i+o_01], cstr[1][1], strlen);
            if (cstr[1] = cstr1) then
            BEGIN
               writeln(
                    cr, 'Offset to TP system code:', dt, '':3,
                    hexl(o_sysseg):9
                      );

               write('Compiled by Borland TP v');

               move(buffer[i-o_02], cstr[2][1], strlen);

               if (cstr[2] = cstr2) then
               BEGIN
                  move(buffer[i+o_03], cstr[3][1], strlen);
                  if (cstr[3] = cstr3) THEN
                     writeln('6.0')
                  ELSE
                     writeln('5.0/5.5');
               END
               ELSE
                  writeln('4.0');
            END;
         END;
      END;
   END; {procedure}

procedure process_exefile;
   CONST
      ofs_whdr  = $3C;      { offset to MS-Window exe file id }
      exwid     = $454E;    { MS-Window exe file id }
   VAR
      o_sign,
      fsize   :longint;
   BEGIN
      if (exe_size = dirfile.size) then
      BEGIN
         print_info;
         if not compressed then
            id_signature;
         writeln;
      END
      else
      BEGIN
{d}      Seek(f, ofs_whdr);        { offset to 'offset to window exe 
signature' }
{d}      BlockRead(f, hdr_size, sizeof(hdr_size));
{d}      if (hdr_size <= dirfile.size) then
         BEGIN
            Seek(f, hdr_size);     { offset to new exe signature }
{d}         BlockRead(f, winhdr, sizeof(winhdr));
         END;
         if (winhdr.id = exwid) then
         BEGIN
            writeln('Dos/MS-Window EXE or DLL file');
            print_info;
            EXIT;
         END
         else
         BEGIN
            print_info;
            writeln(
               cr,
               'file size (', exe_size, ') calculated from EXE header ',
               '(load by DOS upon exec)', cr,
               'doesn''t match with file size (', dirfile.size, ') ',
               'recorded on file directory.', cr, cr,
               '* EXE file saved with extra bytes at eof (e.g. debug info)', cr,
               '* EXE file may contain overlays', cr,
               '* possible a corrupted EXE file', cr
                   );

            EXIT;
         END;
      END;
   END;

procedure id_file;
   CONST
      exeid = $5A4D;    { MS-DOS exe file id }

   VAR
      zero : str2;

   BEGIN
      if (exehdr.id = exeid) then
      BEGIN
         if (exehdr.cs = $FFF0) and
            (exehdr.ip = $0100) and
            (exehdr.ofs_rtbl = $50) or
            (exehdr.ofs_rtbl = $52) then
          BEGIN
             writeln('Compressed by PKLITE');
             compressed := true;
          END;
          if (exehdr.size = 2) and (exehdr.chk_sum = $899D) then
          BEGIN
             writeln( 'Compressed by DIET');
             compressed := true;
          END;
          if (exehdr.Lpage > 0) then
             exe_size := longint(exehdr.Fpages - 1) shl 9+exehdr.Lpage
          else
             exe_size := longint(exehdr.Fpages) shl 9;
          process_exefile;
      END
      else
         writeln('Not EXE file');
   END; {procedure}

CONST
   blocksize = 1; { file r/w block size in one-byte unit }

VAR
   path : dirstr;
   name : namestr;
   ext  : extstr;
   fstr : string[48];
   n    : byte;

BEGIN
   if paramcount < 1 then
      n := 0
   else
      n := 1;

   fsplit(paramstr(n), path, name, ext);
   if (name+ext = '*.*') or (name+ext = '.' ) or (name+ext = '' ) then
      fstr := path+'*.exe'
   else
      if (path+ext = '') then
         fstr := paramstr(n)+'.exe'
      else
         if not boolean(pos('.', ext)) then
         BEGIN
            path := path+name+'\';
            fstr := path+'*.exe';
         END
         else
            fstr := paramstr(n);

    n := 0;
{d} findfirst(fstr, anyfile, dirfile);
    while (doserror = no_error) do
    BEGIN
       if (dirfile.attr and volumeid <> volumeid) and
          (dirfile.attr and directory <> directory) and
          (dirfile.attr and sysfile <> sysfile) then
       BEGIN
          compressed := false;
          Assign(f, path+dirfile.name); {$I-}
{d}       Reset(f, blocksize); {$I+}
          if (IOResult = no_error) then
          BEGIN
             writeln(cr, dirfile.name);
{d}          BlockRead(f, exehdr, sizeof(exehdr), blocks_r);
             if (blocks_r = sizeof(exehdr)) then
                id_file
             else
                writeln('err:main');
             close(f);
             inc(n);
          END;
       END;
{d}    findnext(dirfile);
    END;

    if (n = 0) then
       if doserror = 3 then
          writeln('path not found')
       else
          writeln('file not found')
       else
          writeln(n,' files found');
END.

[Back to DOS SWAG index]  [Back to Main SWAG index]  [Original]