[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]