발자취

악성코드 탐지 시스템 실습 01 - 해시값 하드코딩, 파이썬 파일에 포함된 DB 본문

3-2/악성코드

악성코드 탐지 시스템 실습 01 - 해시값 하드코딩, 파이썬 파일에 포함된 DB

해린 2024. 1. 18. 04:29

악성코드 탐지시스템의 구조를 알아보는 실습을 네 시간에 걸쳐 진행할 예정이다.

본 실습은 '파이썬으로 배우는 Anti-Virus 구조와 원리' 교재의 4~7, 12장에 해당하는 실습이고, 본 게시물에서는 4, 5장의 내용을 다룰 예정이다.

 

네 번에 걸쳐 진행하는 이 실습은 시그니처를 어떻게 관리해서 악성코드를 관리하는지를 볼 수 있는 실습이다. 이 실습을 통해 악성코드 탐지 엔진의 구성을 확인할 수 있다.

간단한 탐지 시스템 실습부터 시작하여, 해당 시스템의 문제점을 해결해나가며 더욱 더 효율적인 방식을 사용하는 탐지 시스템으로 발전시킬 예정이다.

 

악성코드 분석 팀에는 악성코드 특징을 파악하여 시그니처를 만드는 팀과 그 시그니처를 가지고 어떻게 효율적으로 탐지할지에 대한 작업을 하는 팀이 있다. 이 실습을 통해 간단하게나마 후자에 해당하는 내용의 실습을 진행해보며 좀 더 효율적인 악성코드 탐지 시스템에 대해 알아가는 시간을 가질 예정이다.


1. 첫번째 실습

실습에 필요한 코드는 다음과 같다.

# -*- coding:utf-8 -*-
import hashlib
import os

# Original open function
# fp = open('eicar.txt ', 'rb')  

# Modified open function 
# for IntrusionDetection Class
fp = open('fakeVirus1.txt', 'rb') # 바이너리 모드로 읽기
fbuf = fp.read()
fp.close()

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


# Original if-statement
#if fmd5 == '44d88612fea8a8f36de82e1278abb02f':


# Modified if-statement
# for IntrusionDetection Class
if fmd5 == '7ccaad7b5653009985acd6a57a0b189b': #fakeVirus1.txt 파일 MD5와 비교
    print 'Virus'
    os.remove('fakeVirus1.txt')  # 파일을 삭제해서 치료
else:
    print 'No Virus'

4-5.py

 

# -*- coding:utf-8 -*-
import sys
import os
import hashlib

# Original VirusDB
#VirusDB = [
#        '68:44d88612fea8a8f36de82e1278abb02f:EICAR Test',
#        '65:77bff0b143e4840ae73d4582a8914a43:Dummy Test'
#]

# Modified VirusDB
# for IntrusionDetection Class
VirusDB = [
        '49:7ccaad7b5653009985acd6a57a0b189b:FakeVirus1',
        '49:57fa1e44b69f127619bf5c184f40f7b2:FakeVirus2',
        '49:b2c5104283a142d67e213dd7803f11c3:FakeVirus3',
        '49:56d7830ced8ad0bba9451cd74fa20b3f:FakeVirus4',
        '49:186c40e93dfc4a13ffdcbbce34c49cd0:FakeVirus5',
        '49:71c0a6e6d9951f1e5bf1ca3c1f919003:FakeVirus6',
        '49:94de5d86df45f5ef16809dd7adc64bfa:FakeVirus7',
        '49:e57153ccf62f0a0fb2d8f722bcef04c5:FakeVirus8',
        '49:12268add89e0a836f3b9d6986b48500d:FakeVirus9',
        '50:28e18442c9616169601be100d3a1c9f3:FakeVirus10'
]        


vdb = []    # 가공된 악성코드 DB가 저장된다.
vsize = []  # 악성코드의 파일 크기만 저장한다.


# VirusDB를 가공하여 vdb에 저장한다.

def MakeVirusDB():
    for pattern in VirusDB:
        t = []
        v = pattern.split(':') # 세미콜론을 기준으로 자른다.
        t.append(v[1]) # MD5 해시를 저장한다.
        t.append(v[2]) # 악성코드 이름을 저장한다.
        vdb.append(t)   # 최종 vdb에 저장한다.

        size = int(v[0])  # 악성코드 파일크기
        if vsize.count(size) == 0 :  # 이미 해당 크기가 등록되었나?
               vsize.append(size)
               
# 악성코드를 검사한다.
def SearchVDB(fmd5):
    for t in vdb:
        if t[0] == fmd5:   # MD5 해시가 같은지 비교
            return True, t[1]  #악성코드 이름을 함께 리턴

    return False, ''    # 악성코드가 발견되지 않음


if __name__ == '__main__':
    MakeVirusDB()   # 악성코드 DB를 가공한다. 

    if len(sys.argv) != 2:
    #Original print statement
    #    print 'Usage: antivirus.py [file]'
    
    #Modified print statement for IntrusionDetection Class
        print 'Usage: 5-9.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)
    else:
        print '%s : ok' % (fname)

5-9.py

 

1. 실습 절차

우선 실습의 절차는 다음과 같다.

 

1. ~/Desktop/AVCodes 폴더에 정상 파일인 benign1.txt ... benign10.txt 파일과 악성 파일인 fakeVirus1.txt ... fakeVirus10.txt 파일들을 생성한다.

 

2. fakeVirus1.txt 파일을 복사하여 ~/Desktop/AVCodes/Ch4 폴더에 붙여넣고 해당 폴더로 이동한 뒤, 다음의 명령어를 수행한다.

python .\4-5.py

→ fakeVirus1.txt 파일이 삭제된 것을 확인할 수 있다.

 

3. benign2.txt와 fakeVirus2.txt 파일을 복사하여 ~/Desktop/AVCodes/Ch5 폴더에 붙여넣고 해당 폴더로 이동한 뒤, 다음의 명령어를 수행한다.

python .\5-9.py .\benign2.txt

→ benign2.txt 파일이 삭제되지 않은 것을 확인할 수 있다.

 

python .\5-9.py .\fakeVirus2.txt

→ fakeVirus2.txt 파일이 삭제된 것을 확인할 수 있다.

 

 

2. 코드 파일 분석

1. 4-5.py

4-5.py의 내용은 위와 같다.

 

[10] fakeVirus1.txt 파일을 바이너리 모드(rb)로 열어 해당 파일 내용을 읽어들임

[14] MD5 해시 함수를 사용하여 파일 내용의 MD5 해시를 계산

[25] MD5 해시가 '7ccaad7b5653009985acd6a57a0b189b'인 경우 "Virus"를 출력하고 해당 파일을 삭제하게 함.

 

 

2. 5-9.py

[14] VirusDB에 바이러스 길이, 해시값, 이름 정보가 들어있다. 여러 파일들의 정보가 들어있다.

 

[28] 가공된 악성코드 DB

[29] 파일의 크기. 실제 악성코드와 파일 크기를 따로 저장하고 있다.

 

[56] 악성코드 DB 만들기

 

[34] VirusDB라는 리스트에 있는 패턴을 가지고, 루프를 돌면서 이 작업들을 수행한다. :을 기준으로 잘라서 v에 넣어줌. 이걸 활용해서 비어있는 리스트인 t에 해시와 악성코드의 이름을 넣어준다. 그리고 vdb에 t가 들어간다.

[42] vsize에 이미 존재하지 않는 파일 크기라면 vsize에 새롭게 저장

 

[68~] 사이즈가 같은 게 있으면 해시값을 비교해서 악성코드를 검사하고 아니라면 "ok"를 출력하며 삭제하지 않는다.

[49] 해시값과 비교했을 때 일치한다면 true와 악성코드 이름을 같이 리턴하고, 다르면 false를 리턴하며 ''을 리턴한다. 

(true가 나오면 이름을 출력해주고 해당 파일을 삭제하고, false가 나오면 이름 출력하고 "ok" 출력하고 파일 삭제하지 않는 것!)

 

→ 파일 사이즈가 같은 애가 있으면 그때 해시값과 비교해보고, 파일 사이즈가 다르다면 넘긴다. 

모든 파일을 검사하는 방식보다 효율적이다. 탐지하는 시간이 너무 길면 상업적으로 사용하는 것이 불가능하기 때문이다.

 

3. 생각해보기

1. 바이러스 탐지 관점에서 5-9.py 프로그램은 4-5.py 프로그램을 어떠한 방식으로 개선하는가?

4-5.py는 탐지하고자 하는 악성코드의 해시값을 하드코드했다.

이 방식은 확장성이 떨어지며 일반화되어있지 않은 방식이다. (하드코딩된 악성코드만 탐지할 수 있기 때문)

 

반면, 5-9.py는 좀 더 구조화되었다. VirusDB를 만들고, 효율적으로 사이즈와 해시값과 악성코드 이름 자체를 분리시켰다. 그리고 사이즈에 해당하는 리스트를 따로 가져서 사이즈가 매칭되는지를 1차적으로 확인했고, 매칭되는 것들 중에서 해시값을 비교해보는 순서로 행하기 때문에 좀 더 일반화되고 효율적이다. DB에만 추가해주면 되기 때문에 확장성도 있다.

 


이번 시간에는 해시값을 하드코딩하거나, 탐지 시스템 코드 안에 VirusDB를 포함하고 있는 경우의 실습을 진행해보았다.

앞으로의 실습에서 위 방식보다 좀 더 효율적인 방식을 사용하여 악성코드를 탐지하도록 해볼 것이다.