Making an EXE that self-disinfects?

Just starting out? Need help? Post your questions and find answers here.
cbailey
User
User
Posts: 65
Joined: Sat Jun 04, 2005 4:05 am

Making an EXE that self-disinfects?

Post 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?
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Making an EXE that self-disinfects?

Post 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.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
User avatar
Joakim Christiansen
Addict
Addict
Posts: 2452
Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:

Re: Making an EXE that self-disinfects?

Post 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?
I like logic, hence I dislike humans but love computers.
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Making an EXE that self-disinfects?

Post 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).
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post 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.
Last edited by freak on Mon Aug 15, 2005 3:49 pm, edited 1 time in total.
quidquid Latine dictum sit altum videtur
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

This looks interesting, Freak! Thanks!
@}--`--,-- A rose by any other name ..
thefool
Always Here
Always Here
Posts: 5875
Joined: Sat Aug 30, 2003 5:58 pm
Location: Denmark

Post 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.
Last edited by thefool on Mon Aug 15, 2005 3:37 pm, edited 1 time in total.
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

thefool wrote:crackers, not hackers.
True.
@}--`--,-- A rose by any other name ..
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post 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.
quidquid Latine dictum sit altum videtur
thefool
Always Here
Always Here
Posts: 5875
Joined: Sat Aug 30, 2003 5:58 pm
Location: Denmark

Post 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.
danraymond
User
User
Posts: 43
Joined: Wed Jun 28, 2006 6:02 am

MD5 executable check

Post 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
Killswitch
Enthusiast
Enthusiast
Posts: 731
Joined: Wed Apr 21, 2004 7:12 pm

Post 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 :)
Last edited by Killswitch on Thu Oct 26, 2006 2:54 pm, edited 1 time in total.
~I see one problem with your reasoning: the fact is thats not a chicken~
danraymond
User
User
Posts: 43
Joined: Wed Jun 28, 2006 6:02 am

thanks but!

Post by danraymond »

Thanks for help, but still won't compile without errors, have you tried it or just converted it???

thanks

Dan Raymond
Killswitch
Enthusiast
Enthusiast
Posts: 731
Joined: Wed Apr 21, 2004 7:12 pm

Post 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.
~I see one problem with your reasoning: the fact is thats not a chicken~
User avatar
Joakim Christiansen
Addict
Addict
Posts: 2452
Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:

Post by Joakim Christiansen »

Sexy!
I like logic, hence I dislike humans but love computers.
Post Reply