(* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is TurboPower OnGuard
 *
 * The Initial Developer of the Original Code is
 * TurboPower Software
 *
 * Portions created by the Initial Developer are Copyright (C) 1996-2002
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Andrew Haines         andrew@haines.name                        {AH.01}
 *                       conversion to CLX                         {AH.01}
 *                       December 30, 2003                         {AH.01}
 *
 *
 * Boguslaw Brandys      brandys@o2.pl
 *                       conversion to Free Pascal
 *                       June 04, 2006
 *
 *
 * ***** END LICENSE BLOCK ***** *)
{*********************************************************}
{*                  OGPROEXE.PAS 1.13                    *}
{*     Copyright (c) 1996-02 TurboPower Software Co      *}
{*                 All rights reserved.                  *}
{*********************************************************}



{$B-} {Complete Boolean Evaluation}
{$I+} {Input/Output-Checking}
{$P+} {Open Parameters}
{$T-} {Typed @ Operator}


{$mode delphi}{$H+}
{$ASMMODE INTEL}


unit ogproexe;                                                     {AH.01}

interface

uses
  {$IFDEF MSWINDOWS} Windows, {$ENDIF}                             {AH.01}
  {$IFDEF LINUX} BaseUnix, {$ENDIF}                                    {AH.01}
  Classes,
  {$IFDEF MSWINDOWS} MMSystem, {$ENDIF}                            {AH.01}
  SysUtils,
  ogutil;                                                 {AH.01}

type
  {exe signature record}
  PSignatureRec = ^TSignatureRec;
  TSignatureRec = packed record
    Sig1   : DWord;                                                  {!!.07}
    Sig2   : DWord;                                                  {!!.07}
    Sig3   : DWord;                                                  {!!.07}
    Offset : DWord;                                                  {!!.07}
    Size   : DWord;                                                  {!!.07}
    CRC    : DWord;                                                  {!!.07}
    Sig4   : DWord;                                                  {!!.07}
    Sig5   : DWord;                                                  {!!.07}
    Sig6   : DWord;                                                  {!!.07}
  end;

  TExeStatus = (
    exeSuccess,             {no error}
    exeSizeError,           {the file size has changed}
    exeIntegrityError,      {CRC does not match}
    exeNotStamped,          {the exe has not been stamped}
    exeAccessDenied         {share violation}                          {!!.05}
  );

type
  TCheckedExeEvent =
    procedure(Sender : TObject; Status : TExeStatus)
    of object;

  TOgProtectExe = class(TComponent)
  protected {private}
    {property variables}
    FAutoCheck  : Boolean;
    FCheckSize  : Boolean;

    {event variables}
    FOnChecked  : TCheckedExeEvent;

    {property methods}
    function GetAbout : string;                                      {!!.08}
    procedure SetAbout(const Value : string);                        {!!.08}

  protected
    procedure DoOnChecked(Status : TExeStatus);
      dynamic;
    procedure Loaded;
      override;

  public
    constructor Create(AOwner : TComponent);
      override;
    function CheckExe(Report : Boolean) : TExeStatus;
      {-test the exe file for tampering}
    function StampExe(const FileName : string ;  EraseMarker : Boolean) : Boolean;
      {-stamp exe with crc and file size. optionally erase search markers}
    function UnStampExe(const FileName : string) : Boolean;
      {-writes uninitialized signature record. marker must not have been erased}

  published
    {properties}
    property AutoCheck : Boolean
      read FAutoCheck
      write FAutoCheck
      default DefAutoCheck;

    property CheckSize : Boolean
      read FCheckSize
      write FCheckSize
      default DefCheckSize;

    property About : string                                          {!!.08}
      read GetAbout                                                  {!!.08}
      write SetAbout                                                 {!!.08}
      stored False;

    {events}
    property OnChecked : TCheckedExeEvent
      read FOnChecked
      write FOnChecked;
  end;


function IsExeTampered(CheckSize : Boolean) : TExeStatus;
  {-return one of the possible TExeResult states}
function ProtectExe(const FileName : string ;  EraseMarker : Boolean) : Boolean;
  {-stamp exe with crc and file size. optionally erase search markers}
function UnprotectExe(const FileName : string) : Boolean;
  {-writes uninitialized signature record. marker must not have been erased}

{checksum/CRC routines}
procedure UpdateChecksum(var Sum : LongInt;  const Buf;  BufSize : LongInt);
{$IFDEF Win32}
function FileCRC32(const FileName : string) : DWord;                     {!!.07}
{$ELSE}
function FileCRC32(const FileName : string) : DWord;//LongInt;
{$ENDIF}
procedure UpdateCRC32(var CRC : DWord;  const Buf;  BufSize : LongInt);  {!!.07}


implementation


{signature = '!~~@CRC32@~~' used before and after}
const
  StoredSignature : TSignatureRec = (
    Sig1:$407E7E21;  Sig2:$33435243;  Sig3:$7E7E4032;
    Offset:1;  Size:2;  CRC:3;
    Sig4:$407E7E21;  Sig5:$33435243;  Sig6:$7E7E4032);

{CRC32 lookup table}
const
  CRC32Table: array[0..255] of DWord = (                             {!!.07}
    $00000000, $77073096, $ee0e612c, $990951ba, $076dc419, $706af48f, $e963a535, $9e6495a3,
    $0edb8832, $79dcb8a4, $e0d5e91e, $97d2d988, $09b64c2b, $7eb17cbd, $e7b82d07, $90bf1d91,
    $1db71064, $6ab020f2, $f3b97148, $84be41de, $1adad47d, $6ddde4eb, $f4d4b551, $83d385c7,
    $136c9856, $646ba8c0, $fd62f97a, $8a65c9ec, $14015c4f, $63066cd9, $fa0f3d63, $8d080df5,
    $3b6e20c8, $4c69105e, $d56041e4, $a2677172, $3c03e4d1, $4b04d447, $d20d85fd, $a50ab56b,
    $35b5a8fa, $42b2986c, $dbbbc9d6, $acbcf940, $32d86ce3, $45df5c75, $dcd60dcf, $abd13d59,
    $26d930ac, $51de003a, $c8d75180, $bfd06116, $21b4f4b5, $56b3c423, $cfba9599, $b8bda50f,
    $2802b89e, $5f058808, $c60cd9b2, $b10be924, $2f6f7c87, $58684c11, $c1611dab, $b6662d3d,
    $76dc4190, $01db7106, $98d220bc, $efd5102a, $71b18589, $06b6b51f, $9fbfe4a5, $e8b8d433,
    $7807c9a2, $0f00f934, $9609a88e, $e10e9818, $7f6a0dbb, $086d3d2d, $91646c97, $e6635c01,
    $6b6b51f4, $1c6c6162, $856530d8, $f262004e, $6c0695ed, $1b01a57b, $8208f4c1, $f50fc457,
    $65b0d9c6, $12b7e950, $8bbeb8ea, $fcb9887c, $62dd1ddf, $15da2d49, $8cd37cf3, $fbd44c65,
    $4db26158, $3ab551ce, $a3bc0074, $d4bb30e2, $4adfa541, $3dd895d7, $a4d1c46d, $d3d6f4fb,
    $4369e96a, $346ed9fc, $ad678846, $da60b8d0, $44042d73, $33031de5, $aa0a4c5f, $dd0d7cc9,
    $5005713c, $270241aa, $be0b1010, $c90c2086, $5768b525, $206f85b3, $b966d409, $ce61e49f,
    $5edef90e, $29d9c998, $b0d09822, $c7d7a8b4, $59b33d17, $2eb40d81, $b7bd5c3b, $c0ba6cad,
    $edb88320, $9abfb3b6, $03b6e20c, $74b1d29a, $ead54739, $9dd277af, $04db2615, $73dc1683,
    $e3630b12, $94643b84, $0d6d6a3e, $7a6a5aa8, $e40ecf0b, $9309ff9d, $0a00ae27, $7d079eb1,
    $f00f9344, $8708a3d2, $1e01f268, $6906c2fe, $f762575d, $806567cb, $196c3671, $6e6b06e7,
    $fed41b76, $89d32be0, $10da7a5a, $67dd4acc, $f9b9df6f, $8ebeeff9, $17b7be43, $60b08ed5,
    $d6d6a3e8, $a1d1937e, $38d8c2c4, $4fdff252, $d1bb67f1, $a6bc5767, $3fb506dd, $48b2364b,
    $d80d2bda, $af0a1b4c, $36034af6, $41047a60, $df60efc3, $a867df55, $316e8eef, $4669be79,
    $cb61b38c, $bc66831a, $256fd2a0, $5268e236, $cc0c7795, $bb0b4703, $220216b9, $5505262f,
    $c5ba3bbe, $b2bd0b28, $2bb45a92, $5cb36a04, $c2d7ffa7, $b5d0cf31, $2cd99e8b, $5bdeae1d,
    $9b64c2b0, $ec63f226, $756aa39c, $026d930a, $9c0906a9, $eb0e363f, $72076785, $05005713,
    $95bf4a82, $e2b87a14, $7bb12bae, $0cb61b38, $92d28e9b, $e5d5be0d, $7cdcefb7, $0bdbdf21,
    $86d3d2d4, $f1d4e242, $68ddb3f8, $1fda836e, $81be16cd, $f6b9265b, $6fb077e1, $18b74777,
    $88085ae6, $ff0f6a70, $66063bca, $11010b5c, $8f659eff, $f862ae69, $616bffd3, $166ccf45,
    $a00ae278, $d70dd2ee, $4e048354, $3903b3c2, $a7672661, $d06016f7, $4969474d, $3e6e77db,
    $aed16a4a, $d9d65adc, $40df0b66, $37d83bf0, $a9bcae53, $debb9ec5, $47b2cf7f, $30b5ffe9,
    $bdbdf21c, $cabac28a, $53b39330, $24b4a3a6, $bad03605, $cdd70693, $54de5729, $23d967bf,
    $b3667a2e, $c4614ab8, $5d681b02, $2a6f2b94, $b40bbe37, $c30c8ea1, $5a05df1b, $2d02ef8d);


{*** TOgProtectExe ***}

constructor TOgProtectExe.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);

  FAutoCheck := DefAutoCheck;
  FCheckSize := DefCheckSize;
end;

function TOgProtectExe.CheckExe(Report : Boolean) : TExeStatus;
begin
  Result := IsExeTampered(FCheckSize);

  if Report then
    DoOnChecked(Result);
end;

procedure TOgProtectExe.DoOnChecked(Status : TExeStatus);
begin
  if Assigned(FOnChecked) then
    FOnChecked(Self, Status);
end;

function TOgProtectExe.GetAbout : string;                            {!!.08}
begin
  Result := OgVersionStr;
end;

procedure TOgProtectExe.Loaded;
begin
  inherited Loaded;

  if FAutoCheck then
    CheckExe(True);
end;

procedure TOgProtectExe.SetAbout(const Value : string);              {!!.08}
begin
end;

function TOgProtectExe.StampExe(const FileName : string ;  EraseMarker : Boolean) : Boolean;
begin
  Result := ProtectExe(FileName,  EraseMarker);
end;

function TOgProtectExe.UnStampExe(const FileName : string) : Boolean;
begin
  Result := UnprotectExe(FileName);
end;


{$IFDEF Win32}
function IsExeTampered(CheckSize : Boolean) : TExeStatus;
  {-return one of the possible TExeResult states}
var
  Fh      : THandle;
  FileMap : THandle;
  Memory  : PByteArray;
  CRC     : DWord;                                                   {!!.07}
  Buf     : array [0..MAX_PATH - 1] of AnsiChar;
begin
  Result := exeIntegrityError;

  {check if exe has been stamped}
  if (StoredSignature.Offset = 1) and (StoredSignature.Size = 2) and
     (StoredSignature.CRC = 3) then begin
    Result := exeNotStamped;
    Exit;
  end;

  Windows.GetModuleFileName(HInstance, Buf, SizeOf(Buf));
  Fh := CreateFile(Buf, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE,
    nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if (Fh <> INVALID_HANDLE_VALUE) then begin                         {!!.07}
    if CheckSize and (StoredSignature.Size <> Windows.GetFileSize(Fh, nil)) then
      Result := exeSizeError
    else begin
      FileMap := CreateFileMapping(Fh, nil, PAGE_READONLY, 0, 0, nil);
      if (FileMap <> 0) then begin
        Memory := MapViewOfFile(FileMap, FILE_MAP_READ, 0, 0, 0);
        if (Memory <> nil) then begin
          CRC := $FFF00FFF;  {special CRC init}
          UpdateCRC32(CRC, Memory^, StoredSignature.Offset);
          UpdateCRC32(CRC, Memory^[StoredSignature.Offset + SizeOf(TSignatureRec)],
           StoredSignature.Size - (StoredSignature.Offset + SizeOf(TSignatureRec)));
          if (StoredSignature.CRC = not CRC) then
            Result := exeSuccess;
          UnmapViewOfFile(Memory);
        end;
        CloseHandle(FileMap);
      end;
    end;
    CloseHandle(Fh);
  end else begin                                                       {!!.05}
    if GetLastError = ERROR_ACCESS_DENIED then                         {!!.05}
      Result := exeAccessDenied;                                       {!!.05}
  end;                                                                 {!!.05}
end;
{$ELSE}
function IsExeTampered(CheckSize : Boolean) : TExeStatus;
  {-return one of the possible TExeResult states}
const
  BufSize = 4096;
var
  Fh          : Integer;
  TotalRead   : LongInt;
  BytesToRead : LongInt;
  BytesRead   : LongInt;
  Size        : LongInt;
  Buf         : PAnsiChar;
  CRC         : Dword;//LongInt;
begin
  Result := exeIntegrityError;

  {check if exe has been stamped}
  if (StoredSignature.Offset = 1) and (StoredSignature.Size = 2) and   {!!.07}
     (StoredSignature.CRC = 3) then begin                              {!!.07}
    Result := exeNotStamped;
    Exit;
  end;

  Buf := StrAlloc(BufSize);
  try
    //GetModuleFileName(HInstance, Buf, BufSize-1);
    //Buf := ParamStr(0);
    Fh := FileOpen(ParamStr(0), fmOpenRead or fmShareDenyWrite);
    if (Fh >= 0 {HFILE_ERROR}) then begin
      Size := FileSeek(Fh, 0, 2); {get file size}
      FileSeek(Fh, 0, 0);  {reset to beginning of file}
      if CheckSize and (StoredSignature.Size <> Size) then
        Result := exeSizeError
      else begin
        CRC := $FFF00FFF;    {special CRC init}
        TotalRead := 0;
        {read and compute crc up to the signature record}
        while (TotalRead < StoredSignature.Offset) do begin
          if (TotalRead + BufSize <= StoredSignature.Offset) then
            BytesToRead := BufSize
          else
            BytesToRead := (StoredSignature.Offset - TotalRead);
          BytesRead := FileRead(Fh, Buf^, BytesToRead);
          Inc(TotalRead, BytesRead);
          UpdateCRC32(DWord(CRC), Buf^, BytesRead);
        end;
        {jump past signature record}
        FileSeek(Fh, SizeOf(StoredSignature), 1);
        repeat
          BytesRead := FileRead(Fh, Buf^, BufSize);
          Inc(TotalRead, BytesRead);
          UpdateCRC32(DWord(CRC), Buf^, BytesRead);
        until (BytesRead < BufSize);
        if (StoredSignature.CRC = not CRC) then
          Result := exeSuccess;
      end;
      FileClose(Fh);
    end else begin                                                     {!!.05}
      if Fh = -5 then                                                  {!!.05}
        Result := exeAccessDenied;                                     {!!.05}
    end;                                                               {!!.05}

  finally
    StrDispose(Buf);
  end;
end;
{$ENDIF}

{$IFDEF Win32}
function ProtectExe(const FileName : string;  EraseMarker : Boolean) : Boolean;
  {-stamp exe with crc and file size. optionally erase search markers}
var
  Fh      : THandle;
  FileMap : THandle;
  Memory  : PByteArray;
  I, Size : LongInt;
  Sig     : PSignatureRec;
begin
  Result := False;

  Fh := CreateFile(PAnsiChar(FileName), GENERIC_READ or GENERIC_WRITE, 0,
    nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if (Fh <> INVALID_HANDLE_VALUE) then begin
    Size := Windows.GetFileSize(Fh, nil);

    FileMap := CreateFileMapping(Fh, nil, PAGE_READWRITE, 0, 0, nil);
    if (FileMap <> 0) then begin
      Memory := MapViewOfFile(FileMap, FILE_MAP_WRITE, 0, 0, 0);
      if (Memory <> nil) then begin
        for I := 0 to (Size - SizeOf(TSignatureRec)) - 1 do begin
          if (PSignatureRec(@Memory[I]).Sig1 = $407E7E21) and
             (PSignatureRec(@Memory[I]).Sig2 = $33435243) and
             (PSignatureRec(@Memory[I]).Sig3 = $7E7E4032) and
             (PSignatureRec(@Memory[I]).Sig4 = $407E7E21) and
             (PSignatureRec(@Memory[I]).Sig5 = $33435243) and
             (PSignatureRec(@Memory[I]).Sig6 = $7E7E4032) then begin

            {found it}
            Sig := @Memory^[I];

            if EraseMarker then begin
              Sig.Sig1 := Sig.Sig1 xor timeGetTime;
              Sig.Sig2 := Sig.Sig2 xor timeGetTime;
              Sig.Sig3 := Sig.Sig3 xor timeGetTime;
              Sig.Sig4 := Sig.Sig4 xor timeGetTime;
              Sig.Sig5 := Sig.Sig5 xor timeGetTime;
              Sig.Sig6 := Sig.Sig6 xor timeGetTime;
            end;

            Sig.Offset := I;
            Sig.Size := Size;
            Sig.CRC := $FFF00FFF;  {special CRC init}

            {compute crc ignoring the signature record}
            UpdateCRC32(Sig.CRC, Memory^, Sig.Offset);
            UpdateCRC32(Sig.CRC, Memory^[Sig.Offset + SizeOf(TSignatureRec)],
              Sig.Size - (Sig.Offset + SizeOf(TSignatureRec)));
            Sig.CRC := not Sig.CRC;

            Result := True;
            Break;
          end;
        end;
        UnmapViewOfFile(Memory);
      end;
      CloseHandle(FileMap);
    end;
    CloseHandle(Fh);
  end;
end;
{$ELSE}
function ProtectExe(const FileName : string;  EraseMarker : Boolean) : Boolean;
  {-stamp exe with crc and file size. optionally erase search markers}
const
  BufSize = 4096; {must be larger than the size of the signature record}
var
  Fh        : THandle;
  I, Size   : LongInt;
  Sig       : TSignatureRec;
  Buf       : PAnsiChar;
  CRC       : Dword;//LongInt;
  TotalRead : LongInt;
  BytesRead : LongInt;
begin
  Result := False;

  Buf := StrAlloc(BufSize);
  try
    Fh := FileOpen(FileName, fmOpenReadWrite or fmShareDenyWrite);
    if (Fh >= 0 {HFILE_ERROR}) then begin
      Size := FileSeek(Fh, 0, 2);
      FileSeek(Fh, 0, 0);  {reset to beginning of file}
      CRC := $FFF00FFF;    {special CRC init}
      TotalRead := 0;
      repeat
        BytesRead := FileRead(Fh, Buf^, BufSize);

        {search if not found}
        if not Result then begin
          for I := 0 to BytesRead - SizeOf(TSignatureRec) - 1 do begin
            if (PSignatureRec(@Buf[I])^.Sig1 = $407E7E21) and
               (PSignatureRec(@Buf[I])^.Sig2 = $33435243) and
               (PSignatureRec(@Buf[I])^.Sig3 = $7E7E4032) and
               (PSignatureRec(@Buf[I])^.Sig4 = $407E7E21) and
               (PSignatureRec(@Buf[I])^.Sig5 = $33435243) and
               (PSignatureRec(@Buf[I])^.Sig6 = $7E7E4032) then begin

              Result := True;  {found it}

              Sig := PSignatureRec(@Buf[I])^;
              if EraseMarker then begin
                Sig.Sig1 := Sig.Sig1 xor timeGetTime;
                Sig.Sig2 := Sig.Sig2 xor timeGetTime;
                Sig.Sig3 := Sig.Sig3 xor timeGetTime;
                Sig.Sig4 := Sig.Sig4 xor timeGetTime;
                Sig.Sig5 := Sig.Sig5 xor timeGetTime;
                Sig.Sig6 := Sig.Sig6 xor timeGetTime;
              end;

              Sig.Offset := TotalRead + I;
              Sig.Size := Size;

              {update crc up to the start of the signature record}
              UpdateCRC32(DWord(CRC), Buf^, I);
              {update crc for the remaing potion of the buffer (after the signature record)}
              UpdateCRC32(DWord(CRC), Buf[I+SizeOf(TSignatureRec)],
                          BytesRead-(I+SizeOf(TSignatureRec)));
            end;
          end;
        end;

        if not Result then begin
          UpdateCRC32(DWord(CRC), Buf^, BytesRead-SizeOf(TSignatureRec));
          {back up by size of TSignatureRec to insure that split record is found}
          FileSeek(Fh, -SizeOf(TSignatureRec), 1);
          Inc(TotalRead, BytesRead-SizeOf(TSignatureRec));
        end;
      until (BytesRead < BufSize) or Result;

      {scan remaining portion of file}
      if Result then begin
        repeat
          BytesRead := FileRead(Fh, Buf^, BufSize);
          UpdateCRC32(DWord(CRC), Buf^, BytesRead);
        until (BytesRead < BufSize);
      end;

      {save signature record back to the exe file}
      if Result then begin
        Sig.CRC := not CRC;
        FileSeek(Fh, Sig.Offset, 0);
        FileWrite(Fh, Sig, SizeOf(TSignatureRec))
      end;

      FileClose(Fh);
    end;
  finally
    StrDispose(Buf);
  end;
end;
{$ENDIF}


{$IFDEF Win32}
function UnprotectExe(const FileName : string) : Boolean;
  {-writes uninitialized signature record. marker must not have been erased}
var
  Fh      : THandle;
  FileMap : THandle;
  Memory  : PByteArray;
  I, Size : LongInt;
  Sig     : PSignatureRec;
begin
  Result := False;

  Fh := CreateFile(PAnsiChar(FileName), GENERIC_READ or GENERIC_WRITE, 0,
    nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if (Fh <> INVALID_HANDLE_VALUE) then begin
    Size := Windows.GetFileSize(Fh, nil);

    FileMap := CreateFileMapping(Fh, nil, PAGE_READWRITE, 0, 0, nil);
    if (FileMap <> 0) then begin
      Memory := MapViewOfFile(FileMap, FILE_MAP_WRITE, 0, 0, 0);
      if (Memory <> nil) then begin
        for I := 0 to (Size - SizeOf(TSignatureRec)) - 1 do begin
          if (PSignatureRec(@Memory[I]).Sig1 = $407E7E21) and
             (PSignatureRec(@Memory[I]).Sig2 = $33435243) and
             (PSignatureRec(@Memory[I]).Sig3 = $7E7E4032) and
             (PSignatureRec(@Memory[I]).Sig4 = $407E7E21) and
             (PSignatureRec(@Memory[I]).Sig5 = $33435243) and
             (PSignatureRec(@Memory[I]).Sig6 = $7E7E4032) then begin

            {found it}
            Sig := @Memory^[I];

            {restore to uninitialized state}
            Sig^.Offset := 1;                                         {!!.08}
            Sig^.Size := 2;                                           {!!.08}
            Sig^.CRC := 3;                                            {!!.08}

            Result := True;
            Break;
          end;
        end;
        UnmapViewOfFile(Memory);
      end;
      CloseHandle(FileMap);
    end;
    CloseHandle(Fh);
  end;
end;
{$ELSE}
function UnprotectExe(const FileName : string) : Boolean;
  {-writes uninitialized signature record. marker must not have been erased}
const
  BufSize = 4096; {must be larger than the size of the signature record}
var
  Fh        : THandle;
  I         : LongInt;
  Sig       : TSignatureRec;
  Buf       : PAnsiChar;
  Offset    : LongInt;
  BytesRead : LongInt;
  TotalRead : LongInt;
begin
  Result := False;

  Buf := StrAlloc(BufSize);
  try
    Fh := FileOpen(FileName, fmOpenReadWrite or fmShareDenyWrite);
    if (Fh >= 0 {HFILE_ERROR}) then begin
      FileSeek(Fh, 0, 0);  {reset to beginning of file}
      TotalRead := 0;
      repeat
        BytesRead := FileRead(Fh, Buf^, BufSize);

        {search if not found}
        if not Result then begin
          for I := 0 to BytesRead - SizeOf(TSignatureRec) - 1 do begin
            if (PSignatureRec(@Buf[I])^.Sig1 = $407E7E21) and
               (PSignatureRec(@Buf[I])^.Sig2 = $33435243) and
               (PSignatureRec(@Buf[I])^.Sig3 = $7E7E4032) and
               (PSignatureRec(@Buf[I])^.Sig4 = $407E7E21) and
               (PSignatureRec(@Buf[I])^.Sig5 = $33435243) and
               (PSignatureRec(@Buf[I])^.Sig6 = $7E7E4032) then begin

              {found it}
              Sig := PSignatureRec(@Buf[I])^;

              {save position in file}
              Offset := TotalRead + I;
              Result := True;
            end;
          end;
        end;

        if not Result then begin
          {back up by size of TSignatureRec to insure that split record is found}
          FileSeek(Fh, -SizeOf(TSignatureRec), 1);
          Inc(TotalRead, BytesRead-SizeOf(TSignatureRec));
        end;
      until (BytesRead < BufSize) or Result;

      {save signature record back to the exe file}
      if Result then begin
        {restore to uninitialized state}
        Sig.Offset := 1;                                             {!!.08}
        Sig.Size := 2;                                               {!!.08}
        Sig.CRC := 3;                                                {!!.08}

        FileSeek(Fh, Offset, 0);
        FileWrite(Fh, Sig, SizeOf(TSignatureRec))
      end;

      FileClose(Fh);
    end;
  finally
    StrDispose(Buf);
  end;
end;
{$ENDIF}


{checksum/CRC routines}

procedure UpdateChecksum(var Sum : LongInt;  const Buf;  BufSize : LongInt);
var
  Bytes : TByteArray absolute Buf;
  I     : LongInt;
begin
  for I := 0 to BufSize - 1 do
    Sum := Sum + Bytes[I];
end;

{$IFDEF Win32}
function FileCRC32(const FileName : string) : DWord;                 {!!.07}
var
  Fh      : THandle;
  FileMap : THandle;
  Size    : LongInt;
  Memory  : PByteArray;
  Buf     : array [0..MAX_PATH - 1] of AnsiChar;
begin
  Result := $FFFFFFFF;                                               {!!.07}
  StrPLCopy(Buf, FileName, SizeOf(Buf)-1);
  Fh := CreateFile(Buf, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE,
    nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if (Fh <> INVALID_HANDLE_VALUE) then begin                         {!!.07}
    Size := Windows.GetFileSize(Fh, nil);
    FileMap := CreateFileMapping(Fh, nil, PAGE_READONLY, 0, 0, nil);
    if (FileMap <> 0) then begin
      Memory := MapViewOfFile(FileMap, FILE_MAP_READ, 0, 0, 0);
      if (Memory <> nil) then begin
        Result := $FFF00FFF;  {special CRC init}
        UpdateCRC32(Result, Memory^, Size);
        UnmapViewOfFile(Memory);
      end;
      CloseHandle(FileMap);
    end;
    CloseHandle(Fh);
  end;
end;
{$ELSE}
function FileCRC32(const FileName : string) : DWord;//LongInt;
const
  BufSize = 4096;
var
  Fh        : Integer;
  BytesRead : LongInt;
  Buf       : PAnsiChar;
begin
  Buf := StrAlloc(BufSize);
  try
    StrPLCopy(Buf, FileName, BufSize-1);
    Fh := FileOpen(StrPas(Buf), fmOpenRead or fmShareDenyWrite);
    if (Fh >= 0 {HFILE_ERROR}) then begin
      Result := $FFF00FFF;  {special CRC init}
      repeat
        BytesRead := FileRead(Fh, Buf^, BufSize);
        UpdateCRC32(DWord(Result), Buf^, BytesRead);
      until (BytesRead < BufSize);
      FileClose(Fh);
    end;
  finally
    StrDispose(Buf);
  end;
end;
{$ENDIF}

procedure UpdateCRC32(var CRC : DWord;  const Buf;  BufSize : LongInt); {!!.07}
var
  Bytes : TByteArray absolute Buf;
  I     : LongInt;
  B     : Byte;
begin
  for I := 0 to BufSize - 1 do begin
    B := TLongIntRec(CRC).LoLo xor Bytes[I];
    CRC := (CRC shr 8) xor CRC32Table[B];
  end;
end;

end.
