Contributor: TREVOR J. CARLSEN        

{$X+}
Unit selfmod;

 { Author Trevor J Carlsen - released into the public domain 1991            }
 {        PO Box 568                                                         }
 {        Port Hedland                                                       } 
 {        Western Australia 6721                                             }
 {        Voice +61 91 73 2026  Data +61 91 73  2569                         }
 {        FidoNet 3:690/644                                                  }
 { Allows a Program to self modify a Typed Constant in the .exe File.  It    }
 { also perForms an automatic checksum Type .exe File integrity check.       }
 { A LongInt value is added to the end of the exe File.  This can be read by }
 { a separate configuration Program to enable it to determine the start of   }
 { the Programs configuration data area.  to use this the configuration      }
 { Typed Constant should be added immediately following the declaration of   }
 { ExeData.                                                                  }
 
 { Where this Unit is used, it should always be the FIRST Unit listed in the }
 { Uses declaration area of the main Program.                                }
 
 { Requires Dos 3.3 or later.  Program must not be used With PKLite or LZExe }
 { or any similar exe File Compression Programs. It may also cause           }
 { difficulties on a network or virus detection Programs.                    }
 
 { The stack size needed is at least 9,000 Bytes.                            }
 
Interface

Uses
  globals;

Type
  ExeDataType    = Record
                     IDStr      : str7;
                     UserName   : str35;
                     FirstTime  : Boolean;
                     NumbExecs  : shortint;
                     Hsize      : Word;
                     ExeSize    : LongInt;
                     CheckSum   : LongInt;
                     StartConst : LongInt;
                     RegCode    : LongInt;
                   end;
Const
  ExeData : ExeDataType = (IDStr     : 'ID-AREA';
                           UserName  : '';
                           FirstTime : True;
                           NumbExecs : -1;
                           Hsize     : 0;
                           ExeSize   : 0;
                           CheckSum  : 0;
                           StartConst: 0;
                           RegCode   : 0);


{$I p:\prog\freeload.inc} { Creates CodeStr that MUST match RegStr }

{$I p:\prog\registed.inc} { Creates CodeChkStr that MUST hash to RegCode}

Const
  mark  : Byte = 0;

Var
  first : Boolean;

Procedure Hash(p : Pointer; numb : Byte; Var result: LongInt);

Function Write2Exec(Var data; size: Word): Boolean;

Implementation


Procedure Hash(p : Pointer; numb : Byte; Var result: LongInt);
  { When originally called numb must be equal to sizeof    }
  { whatever p is pointing at.  if that is a String numb   }
  { should be equal to length(the_String) and p should be  }        
  { ptr(seg(the_String),ofs(the_String)+1)                 }
  Var
    temp,
    w    : LongInt;
    x    : Byte;

  begin
    temp := LongInt(p^);  RandSeed := temp;
    For x := 0 to (numb - 4) do begin
      w := random(maxint) * random(maxint) * random(maxint);
      temp := ((temp shr random(16)) shl random(16)) +
                w + MemL[seg(p^):ofs(p^)+x];
    end;
    result := result xor temp;
  end;  { Hash }


Procedure InitConstants;
  Var
    f           : File;
    tbuff       : Array[0..1] of Word;
  
  Function GetCheckSum : LongInt;  
    { PerForms a checksum calculation on the exe File }
    Var
      finished  : Boolean;
      x,
      CSum      : LongInt;
      BytesRead : Word;
      buffer    : Array[0..4095] of Word;
    begin
      {$I-}
      seek(f,0);
      finished := False;  CSum := 0;  x := 0;
      BlockRead(f,buffer,sizeof(buffer),BytesRead);
      While not finished do begin             { do the checksum calculations }
        Repeat         { Until File has been read up to start of config area }
          inc(CSum,buffer[x mod 4096]);
          inc(x);
          finished := ((x shl 1) >= ExeData.StartConst); 
        Until ((x mod 4096) = 0) or finished;
        if not finished then                { data area has not been reached }
          BlockRead(f,buffer,sizeof(buffer),BytesRead);          
      end;
      GetCheckSum := CSum;
    end; { GetCheckSum }
    
      
  begin
    assign(f, ParamStr(0));
    {$I-} Reset(f,1);
    With ExeData do begin
      first := FirstTime;
      if FirstTime and (Ioresult = 0) then begin
        Seek(f,2);                   { this location has the executable size }
        BlockRead(f,tbuff,4);
        ExeSize := tbuff[0]+(pred(tbuff[1]) shl 9);
        seek(f,8);                                    {  get the header size }
        BlockRead(f,hsize,2);
        FirstTime := False;
        StartConst := LongInt(hsize+Seg(ExeData)-PrefixSeg) shl 4 + 
                      ofs(ExeData) - 256;
        CheckSum := GetCheckSum;
        Seek(f,StartConst);
        BlockWrite(f,ExeData,sizeof(ExeData));
        seek(f,FileSize(f));
        BlockWrite(f,StartConst,4);
      end
      else
        if GetCheckSum <> CheckSum then begin
          Writeln('File has been tampered with.  Checksum incorrect');
          halt;
        end;
    end;  { With }    
    Close(f); {$I+}
    if Ioresult <> 0 then begin
      Writeln('Unable to initialise Program');
      halt;
    end;  
  end; { InitConstants }


Function Write2Exec(Var data; size: Word): Boolean;
 { Writes a new Typed Constant into the executable File after first checking }
 { that it is safe to do so.  It does this by ensuring that the IDString is  }
 { at the File offset expected.                                              }
  Const
    FName : str40 = '';
  Var
     f          : File;
     st         : str8;
     BytesRead  : Word;
  begin
    if UseCfg then begin
      if length(FName) = 0 then begin
        TempStr    := ParamStr(0);
        TempStrLen := pos('.',TempStr) - 2;
        FName      := TempStr + '. ';
        {                                                                }
        {                         į #255                            }
        {                         į #32                             }
        {                         į #255                            }
        {                        į #255                            }
        { Using the above File name For the configuration File makes the     }
        { deletion of the File difficult For the average user.               }
      end; { if length }
      assign(f, FName);
      if exist(FName) then begin
        {$I-}
        reset(f,1);
        if first then begin
          first := False;
          BlockRead(f, ExeData, ofs(mark)-ofs(ExeData),BytesRead)
        end else
          BlockWrite(f,data,size);
      end else begin
        reWrite(f,1);
        BlockWrite(f,Data,size);
      end;
      close(f);
      {$I+}
      Write2Exec := Ioresult = 0;
    end else begin
      assign(f, ParamStr(0));
      {$I-} Reset(f,1);
      Seek(f,LongInt(ExeData.Hsize+Seg(ExeData)-PrefixSeg) shl 4
                     + ofs(ExeData)- 256);
      BlockRead(f,st,9);
      if st = ExeData.IDStr then { all Ok to proceed } begin
        Seek(f,LongInt(ExeData.Hsize+Seg(data)-PrefixSeg) shl 4
                       + ofs(data)- 256);
        BlockWrite(f,data,size);
        Close(f); {$I+}
        Write2Exec := Ioresult = 0;
      end else
        Write2Exec := False;
    end;
  end; { Write2Exec }
  
begin
  first :=  True;
  if not UseCfg then
    InitConstants
  else
    Write2Exec(ExeData,ofs(mark)-ofs(ExeData));
end.