Page 1 of 2

Making an EXE that self-disinfects?

Posted: Sun Aug 14, 2005 11:40 pm
by cbailey
I had this idea for a program I was working on.. As we should know exactly the size of our EXE, we could have the EXE check itself. If it's too big, it does a search for a known string in the beginning of our EXE. Using that as a point of reference, it truncates everything before it. Then, it truncates everything after the known size of out EXE. Assuming most viruses work by putting most of themselves at the end of the EXE, and a small pointer at the beginning, or everything at the beginning, this would self-disinfect our EXE from most viruses. As I'm still a PureBasic newbie, I don't even know if this could be done from within PureBasic.

Has anyone done anything like this?

Re: Making an EXE that self-disinfects?

Posted: Mon Aug 15, 2005 2:45 am
by PB
I always make my apps check their byte size and "End" them if incorrect,
simply to stop people unpacking them for running (I use UPX on them).
I did this as a primitive sort of anti-cracker device, although I since learnt
it won't stop them at all. I still do it though because, as you said, it's a
good way for the app to know if it's been tampered with in some way.

Re: Making an EXE that self-disinfects?

Posted: Mon Aug 15, 2005 3:11 am
by Joakim Christiansen
PB wrote:I always make my apps check their byte size and "End" them if incorrect,
simply to stop people unpacking them for running (I use UPX on them).
I did this as a primitive sort of anti-cracker device, although I since learnt
it won't stop them at all. I still do it though because, as you said, it's a
good way for the app to know if it's been tampered with in some way.
Sounds handy, could you give us the code for this?

Re: Making an EXE that self-disinfects?

Posted: Mon Aug 15, 2005 4:55 am
by PB
I'm at work without the code at the moment, I'll send it later when I get
home (in about 5 hours from now; got some things to do after work and
before I get home).

Posted: Mon Aug 15, 2005 4:57 am
by freak
Well, i do not think such an auto-disinfection would be that easy. I don't think
a virus puts itself at the absolute beginning of the executable, but rather
somewhere inside the header section, or start of the code section.
So simply cutting of the beginning will not do the trick.

However, checking for changes to the executable and aborting is no problem.
I have played around with this a little and here is what i came up with:

Basically you have a patcher program, that creates a checksum of the executable
and adds it to the executable itself. Then in your code, you must read
that checksum, remove the checksum (because it was not included in the original calculation)
and re-calculate a new checksum of the executable.
If both match, the exe is unchanged. If not, there is a problem.

The new IDE makes automating such patching stuff very easy.

Of course this is no cracker protection, as the code that does the check
can be easily removed by a cracker, but it should do a good job against viruses or a corruped executable.

Ok, first the patcher program:

Code: Select all

; ============================================
; Executable patcher for the MD5 file check
; ============================================
;
; Compile this and add it to the new IDE as two tools for 
; these triggers:
;   After Compile/Run
;   After Create Executable
;
; Commandline: "%EXECUTABLE"
;
; Important: check the "Wait until tool quits" checkbox !!
; Also check the "Hide tool from main menu" checkbox, because
; executing from the menu makes no sense here.
;
; ============================================

; load the given executable file
File$ = ProgramParameter()

If OpenFile(0, File$)
  Size = Lof()
  *Buffer = AllocateMemory(Size)
  
  If *Buffer
    ReadData(*Buffer, Size)    
    
    ; search the executable file for the marker used to insert the MD5 string
    
    *Pointer.BYTE = *Buffer
    *BufferEnd    = *Buffer + Size-32
    While *Pointer < *BufferEnd
      If CompareMemoryString(*Pointer, @"<=== MD5 Executable Check =====>", 0, 32) = 0
      
        ; marker found. now overwrite the marker with spaces
        ; for the MD5 check. (the validation check in the exe does the same)
        ;
        PokeS(*Pointer, Space(32))
        
        ; Get the MD5 sum of the buffer
        ;
        MD5Sum$ = MD5Fingerprint(*Buffer, Size)
        
        ; write the MD5 string into the actual executable file
        FileSeek(*Pointer-*Buffer)
        WriteString(MD5Sum$)
        Break        
        
      EndIf
      *Pointer + 1
    Wend  
  
    FreeMemory(*Buffer)
  Else
    MessageRequester("MD5 Patcher", "Not enough memory!")
  EndIf

  CloseFile(0)
Else
  MessageRequester("MD5 Patcher", "Cannot open executable file!"+Chr(13)+File$)
EndIf
Just compile this to an exe and configure the IDE as described in the comments.

It searches for a "marker" in the executable, that we will later put there.
It then replaces the marker with spaces for the MD5 calculation. Then
the resulting MD5 string is put at the position of the marker.



Ok, now a little include file, that does all the checking...

Code: Select all

; ============================================
; Includefile for the MD5 file check
; ============================================
;
; Include this file into your projects that need
; a check for the executable and call
; ExecutableCheck() to check the file. It returns
; 1 if the file is ok and 0 otherwise.
;
; ============================================

Procedure ExecutableCheck()
  result = 0
  
  ; get the MD5 sum from the datasection
  MD5Sum$ = PeekS(?MD5ExeChecksum)

  ; now we must open our own executable file
  FileName$ = Space(1000)
  If GetModuleFileName_(0, @FileName$, 1000)
  
    file = ReadFile(#PB_Any, FileName$)
    If file
      Size = Lof()
      *Buffer = AllocateMemory(Size)
    
      If *Buffer
        ReadData(*Buffer, Size)
        
        ; in the file, we must search for the real MD5 sum and
        ; replace it with spaces, so the calculations will match.
        
        *Pointer.BYTE = *Buffer
        *BufferEnd    = *Buffer + Size-32
        While *Pointer < *BufferEnd
          If CompareMemoryString(*Pointer, MD5Sum$, 0, 32) = 0
            
            PokeS(*Pointer, Space(32))
            
            ; calculate the new MD5 sum from the executable
            RealMD5$ = MD5Fingerprint(*Buffer, Size)

            ; compare the two
            If RealMD5$ = MD5Sum$
              result = 1
            EndIf
            
            Break
            
          EndIf
          *Pointer + 1
        Wend          
                   
        FreeMemory(*Buffer)
      EndIf
    
      CloseFile(file)
    EndIf
      
  EndIf

  ProcedureReturn result
EndProcedure

; this is our marker or placeholder for the patcher to
; insert the MD5 string.
; What is important is that the string found here is unique and
; does not appear anywhere else in your code. Otherwise, the patcher
; might insert the MD5 at the wrong place!

DataSection  
  MD5ExeChecksum:    
  Data$ "<=== MD5 Executable Check =====>"
EndDataSection
It has both the placeholder for the MD5 string as well as the check
procedure in it.

The only really important thing as noted in the code is that the string that
is originally in the datasection does not appear anywhere else in the code.
(or in another project that is not supposed to be patched by the patcher).
This is important, because our patcher replaces the first occurance it finds
with an MD5 string.

Ok, now for an example:

Code: Select all

XIncludeFile "md5include.pb" ; that is the above include file

If ExecutableCheck()
  MessageRequester("","Executable OK")
Else
  MessageRequester("","Executable changed!")
EndIf

End
If the patcher is setup correctly, you should be able to run it with no trouble.
Now create an executable. it should run ok. Now use a hex-editor
and change just 1 single byte. it will now display the "changed" message.

btw, the patcher will do nothing for files that do not include the above
includefile, so it is ok that the tool is executed with every compiled file.
Of course only as long as the datasection string does not appear in them,
so it must be something unique.

Posted: Mon Aug 15, 2005 3:15 pm
by Dare2
This looks interesting, Freak! Thanks!

Posted: Mon Aug 15, 2005 3:19 pm
by thefool
crackers, not hackers :)

and: i would just use crc32 for this md5 is not needed. however that doesnt matter.

Yes it is a good way to check if any vira infected your program. Of course it will not stop the virus from running though, as it (normally) wants to be run first. But it will give the user a hint that there is something wrong.

Also: it protects from running a corupt file. good for example when dealing with sfx-ers.
For crackers, it wont really help. Simply disabling the check would do it!


However this is a rather good way to do it! And if implemented correctly, protected with self modifying code, jumps [spaghetti code], jumps using stack, fake code etc, then this is a good thing. I would say all protected exe's should at least contain 1 checks like these.

As for self-disinfection, i got an idea:
It IS possible to rename a running exe! Then if the exe is small, contain a compressed copy of ITSELF inside itself. so:

Rename the running corrupt exe, extract to original filename, give error, quit and then delete the renamed file.

Posted: Mon Aug 15, 2005 3:35 pm
by Dare2
thefool wrote:crackers, not hackers.
True.

Posted: Mon Aug 15, 2005 3:40 pm
by freak
> crackers, not hackers.

hey, it was 6am and i was not thinking anymore... of course i know the difference ;)

the reason i choose md5 is that the md5 string is long (and unique) enough
to be found by the patcher and the check routine. If you use crc32 (which is one value only)
you have to put an extra marker to find it in the executable.

Posted: Mon Aug 15, 2005 3:44 pm
by thefool
freak wrote:hey, it was 6am and i was not thinking anymore... of course i know the difference
Oh sure... :P
freak wrote:the reason i choose md5 is that the md5 string is long (and unique) enough
to be found by the patcher and the check routine. If you use crc32 (which is one value only)
you have to put an extra marker to find it in the executable.
Oh sorry about that. Also, the speed doesnt really matter in this case.

MD5 executable check

Posted: Thu Oct 26, 2006 1:46 am
by danraymond
I simply want to check the MD5 thing so I know if a file has been changed. However, the patcher etc in this thread doesn't work with version 4.

Can anyone help me with a revised code as is beyond my coding ability

thanks

Dan Raymond

Posted: Thu Oct 26, 2006 3:49 am
by Killswitch
I'm up late, and I thought I may as well make my insomnia useful. Here's PB4 versions of freak's code.

Patcher Program:

Code: Select all

; ============================================ 
; Executable patcher for the MD5 file check 
; ============================================ 
; 
; Compile this and add it to the new IDE as two tools for 
; these triggers: 
;   After Compile/Run 
;   After Create Executable 
; 
; Commandline: "%EXECUTABLE" 
; 
; Important: check the "Wait until tool quits" checkbox !! 
; Also check the "Hide tool from main menu" checkbox, because 
; executing from the menu makes no sense here. 
; 
; ============================================ 

; load the given executable file 
File$ = ProgramParameter() 

If OpenFile(0, File$) 
  Size = Lof(0) 
  *Buffer = AllocateMemory(Size) 
  
  If *Buffer 
    ReadData(0,*Buffer, Size)    
    
    ; search the executable file for the marker used to insert the MD5 string 
    
    *Pointer.BYTE = *Buffer 
    *BufferEnd    = *Buffer + Size-32 
    While *Pointer < *BufferEnd 
      If CompareMemoryString(*Pointer, @"<=== MD5 Executable Check =====>", 0, 32) = 0 
      
        ; marker found. now overwrite the marker with spaces 
        ; for the MD5 check. (the validation check in the exe does the same) 
        ; 
        PokeS(*Pointer, Space(32)) 
        
        ; Get the MD5 sum of the buffer 
        ; 
        MD5Sum$ = MD5Fingerprint(*Buffer, Size) 
        
        ; write the MD5 string into the actual executable file 
        FileSeek(0,*Pointer-*Buffer) 
        WriteString(0,MD5Sum$) 
        Break        
        
      EndIf 
      *Pointer + 1 
    Wend  
  
    FreeMemory(*Buffer) 
  Else 
    MessageRequester("MD5 Patcher", "Not enough memory!") 
  EndIf 

  CloseFile(0) 
Else 
  MessageRequester("MD5 Patcher", "Cannot open executable file!"+Chr(13)+File$) 
EndIf 
Include file.

Code: Select all

; ============================================ 
; Includefile for the MD5 file check 
; ============================================ 
; 
; Include this file into your projects that need 
; a check for the executable and call 
; ExecutableCheck() to check the file. It returns 
; 1 if the file is ok and 0 otherwise. 
; 
; ============================================ 

Procedure ExecutableCheck() 
  result = 0 
  
  ; get the MD5 sum from the datasection 
  MD5Sum$ = PeekS(?MD5ExeChecksum) 

  ; now we must open our own executable file 
  FileName$ = Space(1000) 
  If GetModuleFileName_(0, @FileName$, 1000) 
  
    file = ReadFile(#PB_Any, FileName$) 
    If file 
      Size = Lof(file) 
      *Buffer = AllocateMemory(Size) 
    
      If *Buffer 
        ReadData(file,*Buffer, Size) 
        
        ; in the file, we must search for the real MD5 sum and 
        ; replace it with spaces, so the calculations will match. 
        
        *Pointer.BYTE = *Buffer 
        *BufferEnd    = *Buffer + Size-32 
        While *Pointer < *BufferEnd 
          If CompareMemoryString(*Pointer, @MD5Sum$, 0, 32) = 0 

            PokeS(*Pointer, Space(32)) 
            
            ; calculate the new MD5 sum from the executable 
            RealMD5$ = MD5Fingerprint(*Buffer, Size) 

            ; compare the two 
            If RealMD5$ = MD5Sum$ 
              result = 1 
            EndIf 
            
            Break 
            
          EndIf 
          *Pointer + 1 
        Wend          
                    
        FreeMemory(*Buffer) 
      EndIf 
    
      CloseFile(file) 
    EndIf 
      
  EndIf 

  ProcedureReturn result 
EndProcedure 

; this is our marker or placeholder for the patcher to 
; insert the MD5 string. 
; What is important is that the string found here is unique and 
; does not appear anywhere else in your code. Otherwise, the patcher 
; might insert the MD5 at the wrong place! 

DataSection  
  MD5ExeChecksum:    
  Data$ "<=== MD5 Executable Check =====>" 
EndDataSection
Example: See above.

Hope that helps :)

thanks but!

Posted: Thu Oct 26, 2006 8:32 am
by danraymond
Thanks for help, but still won't compile without errors, have you tried it or just converted it???

thanks

Dan Raymond

Posted: Thu Oct 26, 2006 2:55 pm
by Killswitch
Sorry mate, there was a mistake when I copied the source over - I pasted the original instead of the stuff I converted.

I haven't tried it, but it should work now.

Posted: Thu Oct 26, 2006 4:07 pm
by Joakim Christiansen
Sexy!