Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.071 Beiträge
 
Delphi 12 Athens
 
#1

Programmdatei auf "Fehler" prüfen

  Alt 23. Nov 2007, 14:18
Heute mal keine Frage,

ich wollte euch nur mal etwas vorstellen was ich mir vor kurzem hab einfallen lassen.

Und zwar einen code, welcher die EXE auf Veränderungen prüft.

Das Ding entdeckt also Dateifehler, welche z.B. durch einen fehlerhaften Download oder andere Dinge entstehen können.


Ein guter Schutz gegen das Patchen einer Datei ist es nicht wirklich, da jeder erfahrene Programmierer dieses "leicht" umgehen kann.

Funktionieren sollte es seit Windows 2000 Pro (Windows Vista, Windows XP or Windows 2000 Professional)
Wenn man das FileMapping durch "normale" Lesezugriffe ersetzt, dann sollte es auch unter älteren Windowsversionen laufen.


Die Funktion:

Es wird nach einer bestimmten Signatur in der Programmdatei gesucht, wo einen MD5-Hash ausgelesen und mit dem Hash der Programmdatei verglichen wird.

Beim Kompilieren wird ein Leerhash einkompiliert, welcher selbstständig beim ersten Programmstart angepaßt wird (also vor Weitergabe des Programmes sollte dieses mindestens einmal gestartet werden).

Aktuell ist keine Selbstlöschroutine eingebaut, weswegen nur das Programm geändert, aber die alte/ungeschützte Version (*.exe.org) nicht gelöscht wird ... dieses müßt/könnt ihr selber machen.

Wer möchte daß die "neue" Programmdatei sofort gestartet wird (ist aber nicht notwendig), der kann die zwei Zeilen mit ShellExecute auskommentieren, damit wird das aktuelle Programm beendet und die neue Version samt aller übergebener Parameter gestartet.
Delphi-Quellcode:
Function SelfCheck: Boolean;
  Const SelfCheckSigLen = 25;
    SelfCheckData: packed Array[0..44] of AnsiChar
      = 'S'#0'e'#1'l'#2'f'#3'C'#4'h'#5'e'#5'c'#4'k'#3'D'#2'a'#1't'#0'a'
      + '>>'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'<<';

  Var FileName: WideString;
    H, Hm: THandle;
    FileSize, i, i2: Integer;
    P: PChar;
    MD5: MD5_CTX;
    B: Boolean;
    W: Cardinal;

  Begin
    Result := False;
    SetLength(FileName, MAX_PATH);
    SetLength(FileName, GetModuleFileNameW(0, PWideChar(FileName), MAX_PATH));
    H := CreateFileW(PWideChar(FileName), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE,
      nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
    FileSize := GetFileSize(H, nil);
    Hm := CreateFileMapping(H, nil, PAGE_READONLY, 0, 0, nil);
    P := MapViewOfFile(Hm, FILE_MAP_READ, 0, 0, 0);
    CloseHandle(H);
    Try
      If P = nil Then Exit;
      For i := 0 to FileSize - Length(SelfCheckData) do
        If (P + i)^ = SelfCheckData[0] Then Begin
          B := True;
          For i2 := 1 to SelfCheckSigLen + 1 do
            If (P + i + i2)^ <> SelfCheckData[i2] Then Begin
              B := False;
              Break;
            End;
          For i2 := SelfCheckSigLen + 2 + SizeOf(MD5.digest) to SelfCheckSigLen + 3 + SizeOf(MD5.digest) do
            If (P + i + i2)^ <> SelfCheckData[i2] Then B := False;
          If B Then Begin
            MD5Init(MD5);
            MD5Update(MD5, P, i);
            MD5Update(MD5, P + i + Length(SelfCheckData), FileSize - i - Length(SelfCheckData));
            MD5Final(MD5);
            Result := True;
            For i2 := 0 to High(MD5.digest) do
              If PByte(P + i + SelfCheckSigLen + 2 + i2)^ <> 0 Then Begin
                Result := False;
                Break;
              End;
            If Result Then Begin
              DeleteFileW(PWideChar(FileName + '.old'));
              MoveFileW(PWideChar(FileName), PWideChar(FileName + '.old'));
              CopyFileW(PWideChar(FileName + '.old'), PWideChar(FileName), False);
              H := CreateFileW(PWideChar(FileName), GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE,
                nil, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
              If Integer(SetFilePointer(H, i + SelfCheckSigLen + 2, nil, FILE_BEGIN)) = i + SelfCheckSigLen + 2 Then
                WriteFile(H, @MD5.digest, SizeOf(MD5.digest), @W, nil);
              CloseHandle(H);
              //ShellExecuteW(0, 'open', PWideChar(FileName), GetCommandLineW, nil, SW_SHOW);
              //Halt;
              Exit;
            End;
            Result := True;
            For i2 := 0 to High(MD5.digest) do
              If PByte(P + i + SelfCheckSigLen + 2 + i2)^ <> MD5.digest[i2] Then Begin
                Result := False;
                Break;
              End;
            Exit;
          End;
        End;
    Finally
      UnmapViewOfFile(P);
      CloseHandle(Hm);
    End;
  End;
Zum Verwenden der Funktion muß sie nur aufgerufen und das Funktionsereignis ausgewertet werden.

Hier eine einfache VCL-lose Version, welche bei einer geänderten/defekten Programmdatei eine MessageBox anzeigt und das Programm beendet.
Delphi-Quellcode:
Var Params: TMsgBoxParamsA;

If not SelfCheck Then Begin
  ZeroMemory(@Params, SizeOf(TMsgBoxParamsA));
  Params.cbSize := SizeOf(TMsgBoxParamsA);
  Params.hInstance := HInstance;
  Params.lpszText := 'self check: file is corrupt'#13#10'{Link zu einer intakten Programmversion}';
  Params.lpszCaption := '{euer Programmname als Überschrift}';
  Params.dwStyle := MB_OK or MB_ICONERROR or MB_SETFOREGROUND;
  MessageBoxIndirectA(Params);
  Halt;
End;
Wem eine MD5-Implementation fehlt, der kann gern auf eine seit Win2000pro verfügbare windowsinterne Variante zurückgreifen:
> MSDN-Library durchsuchenMD5Update
Delphi-Quellcode:
Type MD5_CTX = packed Record
    i: Array[0.. 1] of LongWord;
    buf: Array[0.. 3] of LongWord;
    input: Array[0..63] of Byte;
    digest: Array[0..15] of Byte;
  End;

Procedure MD5Init(Var Context: MD5_CTX); StdCall;
  External 'advapi32.dllName 'MD5Init';
Procedure MD5Update(Var Context: MD5_CTX; Input: Pointer; inLen: LongWord); StdCall;
  External 'advapi32.dllName 'MD5Update';
Procedure MD5Final(Var Context: MD5_CTX); StdCall;
  External 'advapi32.dllName 'MD5Final';
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat