StayOnTarget
StayOnTarget

Reputation: 13037

Integrity check of whole Inno Setup installer

We use Inno Setup for our installer. Recently a user reported the following error during installation:

enter image description here

An error occurred while trying to copy a file: the source file is corrupted

This was due to a setup file that was indeed corrupted somehow.

Ideally the setup EXE would have performed some kind of check upon initialization to see if the entire EXE was valid or not. But apparently it only did this on a file-by-file basis. Is it possible to get InnoSetup to do that?


I looked in Inno Setup documentation for keywords like 'check', 'hash', etc. but didn't see anything - perhaps I missed it.

Quite similar question (from about 10 years ago - though specifically asking about MD5): How to implement MD5 check into Inno Setup installer to get 'like NSIS integrity check'? . That question seemed to state that such a check should already be happening. So perhaps the issue is not whether or not the setup EXE is validated but when this information is used / shown to the user. Also the accepted answer seemed quite manual, ideally I'd like Inno to do this itself.

Similar question with a different error message: "Source file corrupted: SHA-1 hash mismatch" error from Inno Setup

Upvotes: 2

Views: 859

Answers (1)

Martin Prikryl
Martin Prikryl

Reputation: 202474

Add a checksum to the installer and verify it when the installer is starting. A standard (and recommended) way to do that is to sign the installer using code signing certificate. It's a must anyway these days.

Easy way to verify the signature is using PowerShell Get-AuthenticodeSignature. You need PowerShell 5.1 for that. It is bundled with Windows 10 Build 14393 (August 2016) and newer. The following code uses that (and skips the check on older versions of Windows).

function InitializeSetup(): Boolean;
var
  WindowsVersion: TWindowsVersion;
  S: string;
  ResultCode: Integer;
begin
  Result := True;

  GetWindowsVersionEx(WindowsVersion);
  Log(Format('Windows build %d', [WindowsVersion.Build]));
  // TODO: Better would be to check PowerShell version
  if WindowsVersion.Build < 14393 then
  begin
    Log('Old version of Windows, skipping certificate check');
  end
    else
  begin
    S := ExpandConstant('{srcexe}');
    // Possibly this check be might need to be more strict
    // (e.g. allowing only letters, digits and dots)
    if (Pos('''', S) > 0) or (Pos('"', S) > 0) then
      RaiseException('Possible code injection');

    S := 'if ((Get-AuthenticodeSignature ''' + S + ''').Status -ne ''Valid'') ' +
         '{ exit 1 }';
    if ExecAsOriginalUser(
         'powershell', '-ExecutionPolicy Bypass -command "' + S + '"',
         '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and
       (ResultCode = 0) then
    begin
      Log('Installer signature is valid');
    end
      else
    begin
      S := 'Installer signature is not valid. Are you sure you want to continue?';
      Result := (MsgBox(S, mbError, MB_YESNO) = IDYES);
    end;
  end;
end;

If you need to support older versions of Windows, you will have to use more complicated methods, like:

See How to check if a file has a digital signature.

Upvotes: 1

Related Questions