Golang : Simple file scaning and remove virus example



Tags : file-scanning anti-virus bytes-reader strings-index signature

Just for fun, for this tutorial, we will simulate a simple anti-virus program. Let's create a simple file scanner that will detect a virus signature and remove the bytes associated with the file virus.

Now, this is a trivial example, it looks for a string that has been identified as a virus body and will remove the entire body of the virus. It won't be able to detect obfuscated/polymorphic virus code. You will have to add your own "advanced" features into the code.

The code example below will attempt to scan a file block by block and report to you where it detected a virus/virii base on a given signature.

Here you go!

antivirus.go


 package main

 import (
 "bytes"
 "fmt"
 "io/ioutil"
 "math"
 "os"
 "strings"
 )

 // because in this example we will scan a small file
 // we just settle for 1KB chunk per block

 const fileChunk = 1024

 var (
 virusSignature = []byte("<hello I am a computer virus. yeah!>")
 startSignature = "<hello"
 endSignature = "yeah!>"
 )

 func main() {
 if len(os.Args) != 2 {
 fmt.Printf("Usage : %s <filename to scan> \n", os.Args[0])
 os.Exit(0)
 }

 fileToScan := os.Args[1]

 fmt.Printf("Scanning %s....\n", fileToScan)

 file, err := os.Open(fileToScan)
 if err != nil {
 fmt.Println("Unable to open file : ", err)
 os.Exit(-1)
 }

 defer file.Close()

 // calculate the file size
 info, _ := file.Stat()

 filesize := info.Size()

 blocks := uint64(math.Ceil(float64(filesize) / float64(fileChunk)))

 // we scan the file for virusSignature
 // block by block

 for i := uint64(0); i < blocks; i++ {

 blocksize := int(math.Min(fileChunk, float64(filesize-int64(i*fileChunk))))
 buf := make([]byte, blocksize)

 fmt.Printf("Scanning block #%d , size of %d\n", i, blocksize)

 file.Read(buf)

 if strings.Contains(string(buf), string(virusSignature)) {
 //fmt.Println(string(buf))
 fmt.Println("Found virus signature at block # : ", i)

 // we want to find the out start and end positions of the signature
 start := strings.Index(string(buf), startSignature)
 end := strings.LastIndex(string(buf), endSignature)

 // sanity check - verify
 fmt.Println("Virus signature size is : ", len(virusSignature))

 // because of LastIndex returning the first index
 // calcuting (end - start) alone will be inaccurate.
 // we need to compensate for len(endSignature)

 virusSize := ((end - start) + len(endSignature))
 fmt.Println("Detected virus size is : ", virusSize)

 // calculate start and end positions(offsets) relative to file
 // you can use this information for ReadAt() or Seek() operations
 // but because this is a simple virus scanner and removal
 // we will just use the bytes.Replace() function below

 startPosition := (i * fileChunk) + uint64(start)
 endPosition := startPosition + uint64(virusSize)
 fmt.Println("Infection start position : ", startPosition)
 fmt.Println("Infection end position : ", endPosition)

 //ok, let us create a new file without the virus signature
 // ignoring error for simplicty sake
 infectedFile, _ := ioutil.ReadFile(fileToScan)

 //
 cleanedFile := bytes.Replace(infectedFile, virusSignature, []byte(""), -1)

 if err = ioutil.WriteFile("cleanedFile", cleanedFile, 0666); err != nil {
 fmt.Println(err)
 os.Exit(-1)
 }

 fmt.Printf("Disinfected %s and saved clean file to cleanedFile.\n", fileToScan)

 }
 }
 }

Sample output of scanning a text file:

./antivirus suspiciousFile.go

Scanning suspiciousFile.go....

Scanning block #0 , size of 196

Found virus signature at block # : 0

Virus signature size is : 36

Detected virus size is : 36

Infection start position : 76

Infection end position : 112

Disinfected suspiciousFile.go and saved clean file to cleanedFile.

and the suspiciousFile.go / cleanedFile content.

 package main

 import "fmt"

 func virusFunc() {

 greeting := []byte("<hello I am a computer virus. yeah!>")

 fmt.Println(string(greeting))
 }

 func main() {
 fmt.Printf("Hello World!\n")
 }

cleanedFile - notice the diff?

 package main

 import "fmt"

 func virusFunc() {

 greeting := []byte("")

 fmt.Println(string(greeting))
 }

 func main() {
 fmt.Printf("Hello World!\n")
 }

try scanning a binary file that has the virus signature, it works as well.

Happy coding!

References:

https://www.socketloop.com/tutorials/golang-read-a-text-file-and-replace-certain-words

https://socketloop.com/references/golang-os-file-read-and-seek-functions-example

https://www.socketloop.com/references/golang-bytes-reader-readat-function-example

https://golang.org/pkg/strings/#LastIndex

https://golang.org/pkg/strings/#Index

https://www.socketloop.com/tutorials/how-to-generate-checksum-for-file-in-go

  See also : Golang : Convert file content to Hex



Tags : file-scanning anti-virus bytes-reader strings-index signature

By Adam Ng

IF you gain some knowledge or the information here solved your programming problem. Please consider donating to the less fortunate or some charities that you like. Apart from donation, planting trees, volunteering or reducing your carbon footprint will be great too.


Advertisement