Wie erkennt ein Programm ob es infiziert ist ? by SnakeByte [ SnakeByte@kryptocrew.de ] Dieses Tutorial richtet sich an die Programmieren unter euch, die eine Viruserkennung in ihr Programm einbinden wollen, so daß es eine Warnung ausgibt, wenn das Programm von einem Virus infiziert wurde. Dies kann auf mehrere Arten geschehen und ich werde euch hier ein paar Beispiele in Assember und Visual Basic zeigen. Dies benötigt normalerweise nicht viel Platz und kann euch helfen den Benutzer darüber zu informieren, daß euer Programm vielleicht verändert wurde, egal ob es nun gecrackt, mit einem Virus infiziert oder mit einem File-Joiner bearbeitet worden ist. Zuallererst muss man sich darüber Gedanken machen, was ein Virus verändert, wenn er eine Datei befällt: - Größe - Anfangswerte der Register - Datei an sich Fangen wir zuerst mit der Größe an. Das Problem hierbei ist, das sie bis zum Ende der Entwicklung des Programmes nicht feststeht. Auch beim Einfügen des Checks kann es noch zu Veränderungen der Größe kommen, so das es ausser bei Assemblerprogrammen recht schwer ist, diese als feste Größe in das Programm einzufügen. In Assemblerprogrammen kann man allerdings irgendeinen Wert nehmen und diesen nachträglich per Hexeditor recht einfach ändern. Eine Routine zum überprüfen der eigenen Größe würde in Win32Asm zum Beispiel so aussehen: -------8<------------ .Code lea esi, DateiName lea eax, [WIN32_FIND_DATA] push eax push esi call FindFirstFileA push eax call FindClose cmp dword ptr [WFD_nFileSizeLow], 12345678h .Data DateiName db "NamedesProgs",0 FILETIME STRUC FT_dwLowDateTime dd ? FT_dwHighDateTime dd ? FILETIME ENDS WIN32_FIND_DATA label byte WFD_dwFileAttributes dd ? WFD_ftCreationTime FILETIME ? WFD_ftLastAccessTime FILETIME ? WFD_ftLastWriteTime FILETIME ? WFD_nFileSizeHigh dd ? WFD_nFileSizeLow dd ? WFD_dwReserved0 dd ? WFD_dwReserved1 dd ? WFD_szFileName db 260d dup (?) WFD_szAlternateFileName db 13 dup (?) WFD_szAlternateEnding db 03 dup (?) -------8<------------ Für die 12345678h setzt man dann später die richtige Größe ein. Man sollte hier auf jeden Fall einen Wert wie 12345678h verwenden, da sich dieser mit einem Hexeditor später leicht finden lässt. In Hochsprachen wie Visual Basic ist es schwer per Hexeditor einen Wert wiederzufinden. Hier ist es sinnvoller eine .INI Datei mit dem Programm zu liefern, die die Dateigröße enthält. Oder man erstellt diese .INI Datei beim Ersten starten des Programmes, wobei hier die Gefahr besteht, das das Programm schon vorher infiziert wurde. Ok, hier das Ganze also in VB: -------8<------------ Public Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileA" (ByVal lpFileName As String, lpFindFileData As WIN32_FIND_DATA) As Long Public Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long Public Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long Public Type FILETIME dwLowDateTime As Long dwHighDateTime As Long End Type Public Type WIN32_FIND_DATA dwFileAttributes As Long ftCreationTime As FILETIME ftLastAccessTime As FILETIME ftLastWriteTime As FILETIME nFileSizeHigh As Long nFileSizeLow As Long dwReserved0 As Long dwReserved1 As Long cFileName As String * 260 cAlternate As String * 14 End Type Private Function SelfCheck() As Boolean ' Gibt ein True zurück, wenn die Größe gleich ist, ansonsten False ' wenn die Datei verändert wurde Dim finddata As WIN32_FIND_DATA Dim x As Long Dim z As Long Dim Size1 as string Dim Size2 as string SelfCheck = False x = FindFirstFile("MyFile.exe", finddata) DoEvents If x <> -1 Then Size1 = finddata.nFileSizeLow z = FindClose(x) x = GetPrivateProfileString("StartCheck", "Size", " ", Size2, 255, "MyFile.ini") Size2 = Left$(Size2, InStr(Size2, Chr$(0)) - 1) If Size1 = Size2 then SelfCheck = True End Function -------8<------------ Die Anfangswerte der Register lassen sich auch nur in Assembler überprüfen, hier hat EAX den gleichen Wert wie die Anfangs EIP. Dies lässt sich mit wenig Code überprüfen: -------8<------------ Main: cmp eax, offset Main jne Infected ; ; Normales Prog.. ; Infected: ; Error MSG ! -------8<------------ Der Check an sich hat also nur 7 Bytes, ich denk die kann man Opfern. Zwar speichern die meisten Viren die Register beim Start und setzen sie in ihren Originalzustand zurück, aber da der Einsprungspunkt in den Code geändert wurde und nun auf den Virus zeigt, tut dies auch EAX ;) Das wohl sicherste ist per CRC32 oder ähnlichem eine Checksumme für die gesamte Datei zu erstellen und diese per Hexeditor in das Programm einzufügen bzw aus einer Datei zu lesen. Dadurch wird das Programm auf jede Änderung aufmerksam. ( kann auch gegen ein Cracken des Programmes schützen [ jedenfalls bei schlechten Crackern ] ;) Der vielleicht einfachste Weg ist es dieses mit einem API zu machen: CheckSumMappedFile aus der ImageHlp.dll Dazu wird die Datei geöffnet und gemappt. Dann wird die API aufgerufen, die die Checksumme der Datei ausgibt. Diese kann man dann mit der vorher gespeicherten Checksumme vergleichen. Sind sie gleich ist die Datei im Orignialzustand, wenn nicht sollte das Programm reagieren. Einige werden nun sagen, ok, aber was ist mit Kompagnion Viren, die die Datei nur umbenennen ? Ok, zum einen werden unsere Tests fehlschlagen, die den Namen der Datei benutzen um die Größe zu ermitteln. Zum anderen leistet hier ein anderer API gute Dienste: GetCommandLineA Gibt die Kommandozeile mit der das Programm gestartet wurde zurück. "C:\Pfad\Programm.exe /parameter 1 /wasauchimmer" Durch diese kann man zum einen den aktuellen Namen erhalten, und wenn dieser nicht mit dem Originalnamen übereinstimmt sucht man nach "Originalname.*" und wenn dann eine EXE, COM oder BAT Datei gefunden wird, kann man recht sicher sein, das hier ein Kompagnion Virus seine Finger im Spiel hatte. Was ist mit Stealth Viren ? Nun unter Windows hab ich noch keinen Stealth Virus gesehen, aber gegen diese hilft im Endeffekt nur das überprüfen des Anfangswertes von EAX, da diese die Dateigröße ja zum Originalwert verändern. Das wars dann auch schon wieder für dieses Mal. cu SnakeByte