본문 바로가기

Project/Anti-Virus (Python)

5. 다양한 악성코드 진단/치료

5.1 새로운 악성코드 준비

 이전에 전용백신 테스트를 위해 'eicar.txt' 파일을 만들어 사용하였다. 지금부터는 하나의 악성코드가 아닌 다양한 악성코드를 사용하여 백신 테스트를 해볼 것이기 때문에 또 다른 악성코드 파일을 준비해야 한다. 실제로 사용하는 악성코드 또는 실시간 감지기에 탐지되는 악성코드 테스트 파일은 사용하지 않고, 별도로 만들어서 사용하도록 하겠다. 파일 이름은 책에서 사용한 파일과 같은 'dummy.txt'이다.



'dummy.txt' 파일은 63바이트로 MD5 해시값을 미리 알아두어야 한다. MD5 해시값을 찾아주는 코드는 따로 'hmake.py'라는 이름으로 만들어 두었다.

import hashlib
import sys

def hmake():
    if len(sys.argv) != 2:
        print 'Usage : hmake.py [file]'
        return

    fname = sys.argv[1]

    fp = open(fname, 'rb')
    fbuf = fp.read()
    fp.close()

    m = hashlib.md5()
    m.update(fbuf)
    fmd5 = m.hexdigest()

    print fmd5

if __name__ == '__main__':
    hmake()






5.2 다양한 악성코드 진단

 이제 두 개의 악성코드 파일이 준비되었다. 지금은 두 개의 악성코드이지만, 하나씩 하나씩 추가하다보면 상당히 많은 악성코드들을 백신에서 비교해야 할 것이다. 이 때, if문을 사용해 하나씩 추가하게 되면 매 악성코드 추가시마다 if문이 하나씩 증가하게 되어 상당히 비효율적인 코드가 될 것이다. 그래서 이 책에서는 리스트라는 자료구조을 사용한다. 파이썬의 리스트는 C언어의 배열 문법과 유사하지만, 사용자가 쓰기 훨씬 편리한 자료구조로 알아두면 될 것이다. 배열의 경우에는 요소를 같은 자료형의 데이터만 담을 수 있는 반면에 리스트는 다른 자료형이더라도 담을 수 있어서 상당히 편리하다.


5.2.1 악성코드 데이터베이스 가공

 'VirusDB'라는 리스트에 악성코드의 해시값과 이름을 '해시값:악성코드 이름'의 형태로 입력했다. 현재 가지고 있는 악성코드는 두 개이기 때문에 리스트에는 두 개의 요소가 존재하는 것이다. 백신이 리스트에서 하나의 요소를 가지고 와 해시값을 비교하기 위해서는 ':'을 기준으로 리스트의 요소를 또 나누어야 한다. 매번 이런식으로 비교하는 것은 백신의 구동 속도를 떨어뜨리는 것이다. 백신이 리스트의 요소를 가져와서 다시 분리하기 전에 미리 해시값과 악성코드 이름을 분리해두면 진단 속도가 더 빨라질 것이다.


 이 책에서는 'VirusDB'의 요소를 먼저 하나 불러와 이 요소를 ':'으로 나눈 후 해시값과, 악성코드 이름을 요소로 가지는 리스트를 만든 후 이를 'vdb' 리스트의 요소로 넣는 것이다. 즉, 'vdb'리스트는 리스트를 요소로 갖는 리스트인 것이다.


5.2.2 악성코드 진단

# -*- coding:utf-8 -*- import sys import os import hashlib #악성코드 데이터베이스 VirusDB = [ '해시값1:악성코드 이름1' '해시값2:악성코드 이름2' ] #악성코드 데이터 베이스를 가공한 정보 vdb = [] #vdb에 넣을 정보를 만드는 함수 def MakeVirusDB(): for pattern in VirusDB t = [] #해시값과, 악성코드 이름을 분리할 리스트 v = pattern.split(':') #':'을 기준으로 요소를 나눈다 #나눈 요소들을 하나로 연결한다. t.append(v[0]) t.append(v[1]) #vdb의 요소로 넣는다 vdb.append(t) #검사할 파일의 해시값을 넘겨받아 악성코드인지 검사 def SearchVDB(fmd5): for t in vdb: if t[0] == fmd5: return True, t[1] #악성코드일 시, True와 악성코드 이름을 반환 return False, '' #아닐 시, False만 반환 if __name__ == '__main__': MakeVirusDB() #제일 처음 악성코드 데이터 베이스를 가공한다 if len(sys.argv) != 2: print 'Usage : antivirus.py [file]' exit(0) fname = sys.argv[1] #파일이름을 확보한다 #확보한 파일이름을 이름으로 가진 파일의 내용을 획득한다 fp = open(fname, 'rb') buf = fp.read() fp.close() #파일 내용을 해시값으로 변환한다 m = hashlib.md5() m.update(buf) fmd5 = m.hexdigest() #변환한 해시값을 이용해 바이러스 검사를 실시한다, ret, vname = SearchVDB(fmd5) if ret == True: print '%s : %s' % (fname, vname) os.remove(fname) else: print '%s : ok' % (fname)






5.3 백신 검사 속도 높이는 방법 구상

 백신의 중요한 점 중 하나는 검사 속도이다. 아무리 악성코드를 잘 잡아낸다 한들 속도가 느리면 사용하는 사용자 입장에서는 고물단지나 마찬가지이다.


5.3.1 검사 속도가 느린 이유 파악

이 책에서는 백신의 속도를 높이기 위한 내용을 아래와 같이 설명하였다.

  • 파일의 open()을 최대한 자제할 것
  • read()는 파일의 전체 내용을 읽기 때문에 무조건 read()로 파일을 읽어오진 말 것
  • 악성코드 데이터베이스와의 비교 횟수를 최대한 줄일 것

위의 소스코드를 보면 악성코드를 비교하기 위해 항상 파일을 열고 읽는 작업을 반복한다. 이렇게 되면 속도가 느려진다는 것이다.


5.3.2 파일 크기를 활용한 검사 속도 높이기

 백신은 어떤 파일을 비교할지 미리 알 수 없기 때문에 비교할 파일에 대한 크기 정보는 알 수 없다. 하지만, 비교 대상인 악성코드 파일에 대한 정보는 알 수 있기 때문에 악성코드 파일 크기를 활용해서 검사 속도를 높이는 방법에 대해서 알아보겠다. 처음 'VirusDB' 리스트에는 해시값과 악성코드 이름만 들어가 있었다. 이번에는 여기에 악성코드 파일 크기까지 추가해보겠다. '크기:해시값:악성코드 이름'의 형식으로 만들었다. 이렇게 만들게 되면 비교할 파일의 크기만 알 수 있으면 악성코드인지 아닌지 크기만으로 걸러낼 수가 있게 된다. 즉, 악성코드의 파일 크기와 비교할 파일의 크기가 다르다면 이 파일은 악성코드가 아닌 것이 되기 때문에 비교할 필요가 없으므로 파일을 열 필요가 없는 것이다. 불필요하게 파일을 열고 읽는 작업을 줄이게 됨으로써 백신의 속도가 증가된 것이다.

파일 크기를 구하는 함수는 os.path.getsize 함수이다.

# -*- coding:utf-8 -*- import sys import os import hashlib #악성코드 데이터베이스 VirusDB = [ '크기:해시값1:악성코드 이름1' '크기:해시값2:악성코드 이름2' ] #악성코드 데이터 베이스를 가공한 정보 vdb = [] #악성코드들의 크기를 저장할 리스트 vsize = [] #vdb에 넣을 정보를 만드는 함수 def MakeVirusDB(): for pattern in VirusDB t = [] #해시값과, 악성코드 이름을 분리할 리스트 v = pattern.split(':') #':'을 기준으로 요소를 나눈다 #나눈 요소들을 하나로 연결한다. t.append(v[0]) t.append(v[1]) #vdb의 요소로 넣는다 vdb.append(t) size = int(v[0]) #악성코드 파일 크기에 대한 정보가 만약에 없다면 추가한다 if vsize.count(size) == 0: vsize.append(size) #검사할 파일의 해시값을 넘겨받아 악성코드인지 검사 def SearchVDB(fmd5): for t in vdb: if t[0] == fmd5: return True, t[1] #악성코드일 시, True와 악성코드 이름을 반환 return False, '' #아닐 시, False만 반환 if __name__ == '__main__': MakeVirusDB() #제일 처음 악성코드 데이터 베이스를 가공한다 if len(sys.argv) != 2: print 'Usage : antivirus.py [file]' exit(0) fname = sys.argv[1] #파일이름을 확보한다 size = os.path.getsize(fname) #배교할 파일의 크기를 구한다 #확보한 파일이름을 이름으로 가진 파일의 내용을 획득한다 #악성코드 크기와 파일이름이 같은 경우 검사 실행 if vsize.count(size): fp = open(fname, 'rb') buf = fp.read() fp.close() #파일 내용을 해시값으로 변환한다 m = hashlib.md5() m.update(buf) fmd5 = m.hexdigest() #변환한 해시값을 이용해 바이러스 검사를 실시한다, ret, vname = SearchVDB(fmd5) if ret == True: print '%s : %s' % (fname, vname) os.remove(fname) else: print '%s : ok' % (fname)






5.4 정리

 하나의 악성코드가 아닌 다양한 악성코드를 검사하기 시작하면서 고려해야할 점이 많아진다. 그 중 가장 신경 써야 할 것 중 하나가 바로 백신의 속도이다. 이를 해결하기 위해 개발자는 많은 노력을 해야한다.


 이 책에서는 백신의 속도를 줄이기 위해 파일을 열고 읽는 작업을 최소화하였다. 악성코드 데이터 베이스에 악성코드의 크기를 추가하는 방법을 사용하였다.





※ 참고 서적 : 파이썬으로 배우는 Anti-Virus 구조와 원리 (최원혁 지음)

'Project > Anti-Virus (Python)' 카테고리의 다른 글

7. 악성코드 진단/치료 모듈 분리  (1) 2019.04.11
6. 악성코드 패턴 분리하기  (0) 2019.04.04
4. 전용백신 개발하기  (0) 2019.03.16
3. 개발환경 구축  (0) 2019.03.15
2. 악성코드와 백신  (0) 2019.03.15