본 게시글은 책 <악성코드 분석 시작하기> 의 내용을 정리한 글 입니다.

 

악성코드 분석 시작하기 : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

1.7 악성코드 비교와 분류

- 의심 바이너리를 이전 분석 샘플 또는 공개, 사설 저장소에 저장된 샘플과 비교하면 악성코드 군, 악성코드의 특징, 이전 분석 샘플과의 유사성을 파악할 수 있음

- 악성 코드 제작자는 빈번하게 악성코드의 미세한 부분을 변경해 해시 값을 완전히 변경하기 때문에 암호 해시(MD5/SAH1/SHA256)를 통한 분류는 유사한 샘플을 식별하는데 도움이 되지 않음

퍼지 해싱을 이용한 악성코드 분류

- 퍼지 해싱 (Fuzz Hashing)은 파일 유사도를 비교하는 방법

> ssdeep을 이용해 샘플에 대한 퍼지 해시를 생성할 수 있으며, 샘플 간의 유사성 비율을 파악하는 데 도움을 줌

> 서로 다른 MD5 해시 값을 가진 실행 파일의 유사도 비교

> ssdeep의 상세 일치 모드 (-p 옵션)을 사용해 유사도를 알 수 있음

※ b 옵션 : 명령 출력 결과에 파일 이름만 표시하고, 모든 경로 정보는 생략하는 옵션

※ 샘플 3개 중에서 2개의 샘플이 68%의 유사성을 가짐

- 재귀 모드 (-r)를 사용해 디렉터리와 악성코드 샘플을 포함한 하위 디렉터리에서 ssdeep 실행 가능

※ l 옵션 : 파일 이름에 상대 경로 사용

※ a 옵션 : 점수에 관계없이 모두 표시

> 의심 바이너리를 파일 해시 목록과 비교 가능

# 모든 바이너리의 ssdeep 해시는 all_hashes.txt로 리다이렉션
ssdeep * > all_hashes.txt

# [의심 바이너리]는  all_hashes.txt 파일에 있는 모든 해시와 비교
ssdeep -m all_hashes.txt [의심 바이너리]

 

> python에서는 python-ssdeep 라이브러리를 사용해 퍼지 해시 계산 가능

import ssdeep
import sys

if len(sys.argv) != 3:
    print("Usage: python3 fuzzy_hashing_test.py <file1> <file2>")
    sys.exit(1)

# 파일 해시 생성
hash1 = ssdeep.hash_from_file(sys.argv[1])
print("Hash 1:", hash1)

hash2 = ssdeep.hash_from_file(sys.argv[2])
print("Hash 2:", hash2)

# 유사도 비교
similarity = ssdeep.compare(hash1, hash2)
print("Similarity: {}%".format(similarity))

임포트 해시를 이용한 악성코드 분류

- 임포트 해싱 (Import Hashing)은 연관성 있는 샘플과 동일한 공격자 그룹에서 사용한 샘플을 식별하는 데 사용

> 임포트 해시 (Import Hash 또는 imphash) : 실행 파일에 있는 라이브러리/임포트 함수 (API) 명과 특유의 순서를 바탕으로 계산한 해시 값

> 동일한 소스와 동일한 방식으로 컴파일할 경우, 동일한 imphash 값을 갖는 경향이 있음

> pestudio의 imphash 확인 가능

※ 임포트 해싱 관련 참고 링크 : https://cloud.google.com/blog/topics/threat-intelligence/tracking-malware-import-hashing/?hl=en

- python에서는 pefile 모듈을 사용해 임포트 해시 계산 가능

import pefile
import sys

pe = pefile.PE(sys.argv[1])
print(pe.get_imphash())

- 임포트 API와 퍼지 해싱 기술 (Impfuzzy)을 사용해 악성코드 샘플을 분류하는 방법도 있음

- 동일한 imphash를 가진 파일이 반드시 동일한 위협 그룹으로부터 만들어졌다는 것을 의미하지 않음

섹션 해시를 이용한 악성코드 분류

- 임포트 해시와 유사하게 섹션 해시 (Section Hash)도 사용 가능

> pestudio에서 각 센션의 해시 (SHA256) 확인 가능

- python에서는 pefile 모듈을 사용해 섹션 해시 계산 가능

import pefile
import sys

pe = pefile.PE(sys.argv[1])

for section in pe.sections:
    print(f"section name : {section.Name.decode('utf-8')}, section hash(md5) : {section.get_hash_md5()}")

- 악성코드 샘플을 분석할 때 해당 파일의 임포트 해시(Imphash)와 섹션 해시를 생성하고 저장하는 것이 좋음 

> 새로운 샘플을 발견하면 유사점을 판단하고자 해시 비교 가능

YARA를 이용한 악성코드 분류

- 바이너리에 나타나는 고유 문자열과 바이너리 구분자를 기준으로 악성코드를 분류

- YARA악성코드를 식별하고 분류하는 강력하는 도구

> 악성코드 샘플에 포함된 텍스트 또는 바이너리 정보를 기반해 YARA 규칙 (Rule) 생성

> 규칙은 로직을 결정하는 문자열Boolean 표현식의 집합으로 구성

> 규칙을 작성하면 YARA 유틸리티 또는 yara-python을 사용해 작성한 도구로 파일을 스캐닝할 때 해당 규칙을 이용 가능

※ YARA 규칙 작성에 대한 상세 정보 문서 참고 : https://yara.readthedocs.io/en/v3.3.0/gettingstarted.html

 

- YARA 규칙 기초 문법

> ~.yara 파일 생성 후 다음 규칙 작성

rule suspicious_strings
{
strings:
   $a = "Synflooding"
   $b = "Portscanner"
   $c = "Keylogger"
condition:
   ($a or $b or $c)
}

 

구분 설명
규칙 식별자 (Rule Identifier) - 규칙을 설명하는 이름(=suspicious_strings)
- 영문자, 숫자, 밑줄을 포함할 수 있음
> 단, 첫 번째 문자로 숫자를 사용할 수 없음
- 대소문자를 구분하고 128자 초과 불가
문자열 정의 (String Definition) - 규칙의 일부인 문자열 (텍스트, 16진수 또는 정규 표현식)이 정의되는 섹션
> 규칙이 문자열에 의존하지 않을 경우 생략 가능
- 각 문자열은 연속된 영문자, 숫자, 밑줄이 뒤따르는 $ 문자로 구성된 식별자(변수)를 가짐(=$a, $b, $c)
- 해당 변수들을 조건 섹션에서 사용
조건 섹션 (Condition Section) - 규칙의 로직이 위치하는 곳
- 규칙이 일치하거나 일치하지 않는 조건을 지정하는 Boolean 표현식을 포함해야 함

- yara 유틸리티를 사용해 YARA 규칙에 따라 파일을 스캔 (ASCII 문자열, 대소문자 구분)

> ASCII와 유니코드 (와이드 문자) 문자열을 탐지하는 규칙의 경우 문자열 옆에 asciiwide 수식어 지정

> nocase 수식어는 대소문자를 구분하지 않는 매칭

 

- PE 파일 탐색 YARA Rule

> “$mz at 0”은 YARA가 파일이 시작되는 곳에서 4D 5A 시그니처를 찾음 :  PE 파일만 탐색

※ 텍스트 문자열은 "", 16진수 문자열은 중괄호 {}로 묶어야 함

rule PE_Files
{
strings:
   $mz = {4D 5A}
condition:
   $mz at 0
}

 

- 정규 표현식을 사용한 URL 형식의 문자열을 포함한 실행 파일 탐색 YARA rule

rule URL_Detect
{
strings:
   $url_pattern = /http:\/\/[a-zA-Z0-9\.-]+\.(com|net|org)/
condition:
   $url_pattern
}

 

- PE 헤더 이후에 특정 바이너리 패턴 일치 YARA rule

> 16진수 문자열이 파일의 1,024바이트 이후에 오프셋 (PE 헤더를 건너뜀)에서 발견할 경우 동작

> filesize는 파일의 끝을 의미

rule Embedded_Office_Document
{
strings:
   $mz = {4D 5A}
   $a = { D0 CF 11 E0 A1 B1 1A E1 }
condition:
   ($mz at 0) and $a in (1024..filesize)
}

 

- 패커를 탐지하는 데에도 YARA rule

> 패커 탐지 도구 Exeinfo PE는 userdb.txt라 불리는 일반 텍스트 파일에 저장된 시그니처를 사용

> Exeinfo PE가 UPX 패커를 탐지할 때 사용한 시그니처 포맷의 예

※ ep_only = true : Exeinfo PE가 엔트리 포인트 (코드가 실행을 시작하는 위치)의 프로그램 주소에서만 시그니처를 확인해야 함을 의미

[UPX 2.90 (LZMA)]
signature = 60 BE ?? ?? ?? ?? 8D BE ?? ?? ?? ?? 57 83 CD FF 89 E5 8D 9C 24 ?? ?? ?? ?? 31 C0 50 39 DC 75 FB 46 46 53 68 ?? ?? ?? ?? 57 83 C3 04 53 68 ?? ?? ?? ?? 56 83 C3 04 53 50 C7 03 ?? ?? ?? ?? 90 90
ep_only = true

 

- 새로운 버전의 YARA는 PE 파일 포맷의 속성과 기능을 사용해 PE 파일을 위한 규칙을 생성할 수 있는 PE 모듈 지원

> 다음 방법을 사용하면 Exeinfo PE의 userdb.txt에 있는 모든 패커 시그니처를 YARA 규칙으로 변환 가능

※ UserDB.txt(PEiD 도구에서 사용)를 YARA 규칙으로 변환하는 Python 코드 참고 : https://github.com/DidierStevens/DidierStevensSuite/blob/master/peid-userdb-to-yara-rules.py

import "pe"
rule UPX_290_LZMA
{
meta :
   description = "detect upx packer 2.90"
   ref = "userdb.txt file from the Exeinfo PE"
strings:
   $a = {60 BE ?? ?? ?? ?? 8D BE ?? ?? ?? ?? 57 83 CD FF 89 E5 8D 9C 24 ?? ?? ?? ?? 31 C0 50 39 DC 75 FB 46 46 53 68 ?? ?? ?? ?? 57 83 C3 04 53 68 ?? ?? ?? ?? 56 83 C3 04 53 50 C7 03 ?? ?? ?? ?? 90 90}
condition:
   $a at pe.entry_point
}

 

- 모든 파일의 패턴을 감지하는 데 사용 가능

rule Gh0stRat
{
	meta
		description = "Gh0stRat_communications"
	
	strings:
		$gst1 = { 8D ?? ?? 5? 8B ?? ?? ?? ?? ?? 81 C? ?? ?? ?? ?? E8 ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 89 ?? ?? ?? ?? ?? 6A ?? 8D ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? C1 ?? ?? 8B ?? ?? ?? ?? ?? 8D ?? ?? ?? ?? ?? ?? 5? 8D ?? ?? E8 ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? C1 ?? ?? 8B ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? ?? 89 ?? ?? 8B ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? C1 ?? ?? 8B ?? ?? ?? ?? ?? 8D ?? ?? ?? ?? ?? ?? 5? 8D ?? ?? E8 ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? C1 ?? ?? 8B ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? ?? 89 ?? ?? 8D ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 83 ?? ?? 0F 85 }
		$gst2 = { 5? 8B ?? 6A ?? 68 ?? ?? ?? ?? 64 ?? ?? ?? ?? ?? 5? 64 ?? ?? ?? ?? ?? ?? 83 ?? ?? 89 ?? ?? 8B ?? ?? E8 ?? ?? ?? ?? 8D ?? ?? E8 ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? 8D ?? ?? 5? E8 ?? ?? ?? ?? 5? 8B ?? ?? 81 C? ?? ?? ?? ?? E8 ?? ?? ?? ?? 6A ?? 6A ?? 6A ?? 6A ?? 8D ?? ?? E8 ?? ?? ?? ?? 5? 8B ?? 89 ?? ?? 8D ?? ?? 5? E8 ?? ?? ?? ?? 8B ?? ?? 81 C? ?? ?? ?? ?? E8 ?? ?? ?? ?? 6A ?? 6A ?? 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 8D ?? ?? 5? E8 ?? ?? ?? ?? 89 ?? ?? 8B ?? ?? 89 ?? ?? C6 ?? ?? ?? 8B ?? ?? E8 ?? ?? ?? ?? 5? 8D ?? ?? E8 ?? ?? ?? ?? 89 ?? ?? C6 ?? ?? ?? 8D ?? ?? E8 ?? ?? ?? ?? 83 ?? ?? ?? 0F 84 }
		$gst3 = { 5? 8B ?? 6A ?? 68 ?? ?? ?? ?? 64 ?? ?? ?? ?? ?? 5? 64 ?? ?? ?? ?? ?? ?? 83 ?? ?? 89 ?? ?? 6A ?? 8B ?? ?? E8 ?? ?? ?? ?? 8D ?? ?? E8 ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? 6A ?? 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 8D ?? ?? 5? E8 ?? ?? ?? ?? 89 ?? ?? 8B ?? ?? 89 ?? ?? C6 ?? ?? ?? 8B ?? ?? E8 ?? ?? ?? ?? 5? 8D ?? ?? E8 ?? ?? ?? ?? 89 ?? ?? C6 ?? ?? ?? 8D ?? ?? E8 ?? ?? ?? ?? 83 ?? ?? ?? 0F 84 }
		$gst4 = { 66 ?? ?? ?? ?? ?? ?? 66 ?? ?? ?? ?? ?? ?? 0F BF ?? ?? ?? ?? ?? 83 ?? ?? 5? E8 ?? ?? ?? ?? 83 ?? ?? 89 ?? ?? ?? ?? ?? 0F BF ?? ?? ?? ?? ?? 5? 8B ?? ?? ?? ?? ?? 5? 8D ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 0F BF ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? C6 ?? ?? ?? 8B ?? ?? ?? ?? ?? 5? 8B ?? ?? ?? ?? ?? 81 C? ?? ?? ?? ?? E8 ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 5? E8 ?? ?? ?? ?? 83 ?? ?? 8D ?? ?? ?? ?? ?? E8 }
		$any_variant = /.{5,16}\x00\x00..\x00\x00\x78\x9c
		
	condition:
		any of (gst*) or ($any_variant)
}

본 게시글은 책 <악성코드 분석 시작하기> 의 내용을 정리한 글 입니다.

 

악성코드 분석 시작하기 : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

1.6 PE 헤더 정보 조사

- 윈도우 실행 파일은 PE/COFF (Portable Executable/Common Object File Format)를 반드시 준수해야 함

 

- PE 파일 포맷

> 윈도우 실행 파일(.exe, .dll, .sys, .ocx, drv)이 사용

> 운영 시스템이 메모리로 로딩할 때 필요한 정보를 가진 일련의 구조체와 하위 컴포넌트

 

- 실행 파일을 컴파일하면 해당 구조체를 설명하는 헤더 (PE 헤더)를 포함

> 실행 파일이 메모리에서 실행될 위치

> 실행 파일의 시작 위치

> 애플리케이션이 의존하는 라이브러리/함수 목록

> 바이너리가 사용하는 리소스와 같은 정보가 포함

 

- PE 파일 구조체를 이해하기 위한 유용한 자료 링크 
> Win32 PE 파일 포맷에 대한 심층적인 조사 - 파트 1 : https://www.delphibasics.info/home/delphibasicsarticles/anin-depthlookintothewin32portableexecutablefileformat-part1
> Win32 PE 파일 포맷에 대한 심층적인 조사 - 파트 2 : https://www.delphibasics.info/home/delphibasicsarticles/anin-depthlookintothewin32portableexecutablefileformat-part2
> PE 헤더와 구조 : https://www.openrce.org/reference_library/files/reference/PE%20Format.pdf
> PE101 - 윈도우 실행 파일 공략 : https://github.com/corkami/pics/blob/master/binary/pe101/pe101ko.pdf

 

- PE 구조와 그 하위 컴포넌트를 검사하고 수정할 수 있는 몇 가지 도구 
> CFF Explorer : https://ntcore.com/explorer-suite/

> PE Internals : https://www.andreybazhan.com/pe-internals.html

> PPEE(puppy) : https://www.mzrst.com/
> PEBrowse Professional : https://download.cnet.com/developer/smidgeonsoft/i-6276008

파일 의존성과 임포트 조사

- 악성코드는 파일, 레지스트리, 네트워크 등과 상호작용하며, 이를 위해 운영 시스템에서 제공하는 함수에 많이 의존

- 윈도우는 API로 불리는 함수를 임포트하며, 상호작용을 위해 동적 링크 라이브러리 (DLL, Dynamic Link Library) 파일이 필요

> DLL에서 여러 함수를 임포트 (Import) 하거나 호출

> 악성코드가 의존하고 있는 DLL과 그런 DLL이 임포트하고 있는 API 함수를 조사하면 악성코드의 기능과 성능, 실행 중 예상할 수 있는 기능을 알 수 있음

 

- 윈도우 실행 파일의 파일 의존성은 PE 파일 구조의 임포트 테이블에 저장

> pestudiolibraries 버튼을 클릭하면 의존성을 가진 모든 DLL 파일과 각 DLL에서 임포트한 함수의 수를 보여줌

> imports 버튼을 클릭하면 DLL에서 임포트한 API를 표시

- 때때로 악성코드는 LoadLibrary() 또는 LdrLoadDLL()과 같은 API를 호출해서 실행 중 명시적으로 DLL 로드 가능

- 또는, GetProcAddress() API를 사용해 함수 주소를 확인할 수 있음

> 실행 중 로드한 DLL의 정보는 PE 파일의 임포트 테이블에 나타나지 않음

> 윈도우 API 함수와 그 함수의 기능 정보 참고 : https://learn.microsoft.com/ko-kr/search/

 

- Python을 이용해 DLL 파일과 임포트한 함수를 나열할 수 있으며, pefile 모듈을 사용

import pefile
import sys

# 명령행 인자 체크
if len(sys.argv) != 2:
    print("Usage: python script.py <filename>")
    sys.exit(1)

mal_file = sys.argv[1]


def enum_import(mal_file):
    try:
        # PE 파일 로드
        pe = pefile.PE(mal_file)
        
        # Import Table이 존재하는지 확인
        if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
            print(f"[+] {mal_file} Import Table Found")
            for entry in pe.DIRECTORY_ENTRY_IMPORT:
                print(f"DLL : {entry.dll.decode('utf-8')} ({len(entry.imports)})")
                for i, imp in enumerate(entry.imports):
                    if imp.name is not None:
                        print(f"  {i + 1}. {imp.name.decode('utf-8')}")
                    else:
                        print(f"  {i + 1}. Ordinal: {imp.ordinal}")
                print()
        else:
            print("[-] No Import Table Found")

    except FileNotFoundError:
        print("[-] File not found.")
    except pefile.PEFormatError:
        print("[-] Invalid PE file.")
    except Exception as e:
        print(f"[-] Error: {e}")


if __name__ == '__main__':
    enum_import(mal_file)

익스포트 (Export) 조사

- 실행 파일과 DLL은 다른 프로그램에서 사용할 수 있는 함수를 익스포트 (Export)할 수 있음

> 일반적으로 DLL은 실행 파일이 임포트할 수 있는 함수 (익스포트)를 노출

> DLL은 단독으로 실행할 수 없기 때문에 호스트 프로세스를 통해 실행

> 공격자는 악의적인 함수를 익스포트하는 DLL을 자주 생성

※ 익스포트 함수명이 매번 악성코드의 기능에 대한 힌트를 주는 것은 아님

 

- pestudio의 exports 메뉴에서 확인 가능

> Python 예시 참조

import pefile
import sys

# 명령행 인자 체크
if len(sys.argv) != 2:
    print("Usage: python script.py <filename>")
    sys.exit(1)

mal_file = sys.argv[1]


def enum_import(mal_file):
    try:
        # PE 파일 로드
        pe = pefile.PE(mal_file)
        
        # Import Table이 존재하는지 확인
        if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
            print(f"[+] {mal_file} Import Table Found")
            for entry in pe.DIRECTORY_ENTRY_IMPORT:
                print(f"DLL : {entry.dll.decode('utf-8')} ({len(entry.imports)})")
                for i, imp in enumerate(entry.imports):
                    if imp.name is not None:
                        print(f"  {i + 1}. {imp.name.decode('utf-8')}")
                    else:
                        print(f"  {i + 1}. Ordinal: {imp.ordinal}")
                print()
        else:
            print("[-] No Import Table Found")
        
        # Export Table이 존재하는지 확인
        if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
            print(f"[+] {mal_file} Export Table Found")
            for entry in pe.DIRECTORY_ENTRY_EXPORT.symbols:
                if entry.name is not None:
                    print(f"DLL : {entry.name.decode('utf-8')}")
                else:
                    print(f"Ordinal : {entry.ordinal}")
        else:
            print("[-] No Export Table Found")

    except FileNotFoundError:
        print("[-] File not found.")
    except pefile.PEFormatError:
        print("[-] Invalid PE file.")
    except Exception as e:
        print(f"[-] Error: {e}")


if __name__ == '__main__':
    enum_import(mal_file)

PE 섹션 테이블과 섹션 조사

- PE 파일의 실제 내용은 섹션 (Section)으로 구분

> 섹션은 코드 (Code)데이터 (Data)를 나타내고, 읽기/쓰기와 같은 메모리 내부 속성을 가짐

구분 설명
코드 - 프로세스가 실행할 명령어를 포함
데이터 - 읽기/쓰기 프로그램 데이터 (전역변수)
- 임포트/익스포트 테이블
- 리소스 등

 

- 각 섹션은 해당 섹션의 목적을 나타내는 고유한 이름을 갖고 있음

> 실행 파일을 컴파일하는 동안 컴파일러는 일관된 섹션 이름을 추가

> PE 파일의 공통 섹션

섹션명 설명
.text 또는 CODE - 실행 코드를 포함
.data 또는 DATA - 읽기/쓰기 데이터와 전역 변수 포함
.rdata - 읽기 전용 데이터를 포함. 때에 따라 임포틑 또는 익스포트 정보 포함
.idata - 존재한다면 임포트 테이블 포함. 존재하지 않으면, .rdata 섹션에서 탐색 가능
.edata - 존재한다면 익스포트 정보 포함. 존재하지 않으면, .rdata 섹션에서 탐색 가능
.rsrc - 실행 파일에서 사용하는 아이콘, 대화창, 메뉴, 문자열 등의 리소스

> 섹션명은 사람을 위한 것으로 운영 시스템에서는 사용하지 않으며, 공격자 또는 난독화 소프트웨어가 섹션명을 변경할 수 있음

 

- 섹션에 대한 정보 (섹션명, 섹션의 위치, 특징)는 PE 헤더에 있는 섹션 테이블 (Section Table)에 존재

> pestudio의 sections 메뉴에서 섹션 테이블에서 추출한 섹션 정보와 그 속성 (읽기/쓰기)을 표시

> 섹션 테이블 필드

필드 설명
name - 섹션 이름
virtual-size - 메모리에 로딩할 때 섹션의 크기 
virtual-address - 섹션을 메모리 어디에서 찾을 수 있는지 나타내는 상대적 가상 주소(실행 파일 베이스 주소에서 얼마나 떨어져 있는지를 나타내는 오프셋)
raw-size - 해당 섹션이 디스크에 존재할 때의 크기
raw-data - 파일에서 해당 섹션을 찾을 수 있는 오프셋 
entry-point - 코드가 실행을 시작하는 RVA(Relatve Virtual Address, 상대적 가상 주소). 일반적으로 .text 섹션에 존재

> 만약 컴파일러에서 생성한 일반적인 섹션명 (.text, .data 등)이 아니라면 의심

> 일반적으로 raw-size와 virtual-size의 크기는 거의 같아야 함

> 만약 raw-size ≒ 0인데, virtual-size가 더 큰 공간을 갖고 있다면, 이는 패킹한 바이너리일 수 있음 (패킹한 바이너리를 실행할 때 패커의 압축 해제 루틴이 런타임 중 압축 해제한 데이터 또는 명령어를 메모리로 복사하기 때문)

컴파일 타임스탬프 조사

- PE 헤더는 바이너리가 컴파일될 때 생성되는 정보를 포함

- 타임스탬프 (Timestamp) 필드를 조사하면 악성코드가 언제 처음 생성됐는지 알 수 있음

> 공격 활동의 타임라인을 작성할 때 도움을 줌

> 공격자가 실제 타임스탬프를 알 수 없도록 수정해 분석 방해 가능

> pestudio의 file-header 메뉴에 “compiler-stamp” 항목에서 확인 가능

PE 리소스 조사

- 아이콘, 메뉴, 대화장사, 문자열과 같이 실행 파일에 필요한 리소스는 실행 파일의 리소스 섹션 (.rsrc)에 저장

> 공격자는 때때로 추가 바이너리, 미끼 문서, 설정 데이터 같은 정보를 리소스 섹션에 저장

> 리소스 해커 (Resource Hacker)는 의심 바이너리에서 리소스를 조사하고 확인 후 추출할 수 있는 도구

본 게시글은 책 <악성코드 분석 시작하기> 의 내용을 정리한 글 입니다.

 

악성코드 분석 시작하기 : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

1.4 문자열 추출

- 문자열 (String) : 파일에 포함된 출력 가능한 ASCII와 유니코드의 순차적인 문자 집합

 

- 문자열 추출은 프로그램 기능과 의심 바이너리 관련 지표에 대한 단서를 제공

> 파일명, URL, 도메인명, IP 주소, 공격 명령어, 제시트리 키 등을 포함

> 악성코드가 파일을 생성한다면 해당 파일명이 바이너리 안에 문자열로 저장

> 공격자가 통제하는 도메인명을 악성코드가 호출한다면 해당 도메인명이 문자열로 저장

> 파일의 목적과 기능에 대한 명확한 단서는 아니지만, 악성코드가 할 수 있는 일에 대한 힌트를 제공

 

- 리눅스에서는 strings 유틸리티를 사용해 문자열을 추출할 수 있음 (기본적으로 최소 4문자 이상인 ASCII 문자열 추출)

> -a 옵션 : 전체파일에서 문자열 추출

> -el 옵션 : 유니코드 추출

> 바이너리에서 유용한 정보를 얻으려면 때때로 ASCII와 유니코드 문자열 모두를 추출해야 함

- 윈도우에서는 ASCII와 유니코드 문자열 모두를 표현해 주는 pestudio를 사용

> pestudio는 훌륭한 PE 분석 도구로 의심 바이너리의 악성코드 초기 점검이 가능

> PE 실행 파일에서 유용한 정보의 여러 힌트를 얻을 수 있음

- FLOSS (FireEye Labs Obfuscated String Solver)는 악성코드에서 난독화된 문자열을 자동을 추출하고 식별하고자 디자인된 도구

> 악성코드는 탐지를 회피하고자 문자열 난독화(String Obfuscation) 기법을 사용

> 난독화가 적용된 경우 strings 유틸리티를 이용해 문자열 추출 불가

1.5 파일 난독화 파악

- 악성코드 제작자는 악성코드 바이너리를 일반적으로 난독화하거나 보호

> 난독화 기술은 바이너리를 탐지/분석하기 어렵게 하기 때문에 추출할 수 있는 문자열이 거의 없음

> 악성코드 제작자는 보안 제품의 탐지 회피 및 분석을 방해하고자 패커 (Packer)크립터 (Cryptor) 같은 프로그램을 자주 사용

 

- 패커 (Packer)

> 실행 파일을 입력받아 실행 파일의 내용을 압축해 난독화하는 프로그램

> 난독화한 파일은 새로운 실팽 파일의 구조체에 저장

> 패킹 (Packing)한 프로그램을 실행하면 압축해제 루틴이 실행되고 메모리에 원본 바이너리를 추출한 후 실행

 

- 크립터 (Cryptor)

> 패커와 유사하나, 실행 파일의 내용을 난독화하고자 압축 대신 암호화를 사용

> 암호화한 내용은 새로운 실행 파일에 저장

> 암호화한 프로그램을 실행하면 실행할 때 복호화 루틴을 실행해 원본 바이너리를 메모리에 추출한 후 실행

 

- UPX 패커 적용

> UPX는 압축 기법을 사용하기 때문에 패킹한 바이너리가 원본 바이너리보다 크기가 작음

> 패킹한 바이너리에 strings 명령을 실행하면 난독화한 문자열을 보여 주고 가치 있는 정보를 노출하지 않음 -> 난독화하는 이유

- 윈도우에서는 Exeinfo PE를 이용해 파일 난독화 탐지

> Exeinfo PE : 윈도우에서 패커를 탐지하는 도구

> 패킹된 파일을 Exeinfo PE에 로딩하면 패킹 정보와 패킹 해제에 사용할 명령어에 대한 힌트도 제공

> 정상적인 실행 파일 대부분은 파일 내용을 난독화하지 않기 때문에 패킹을 했다면 악성코드일 확률이 매우 높음

- 패커, 크립터 탐지를 도울 수 있는 다른 CLI와 GUI 도구 
> TrID
> TRIDNet
> Detect it Easy
> RDG Packer Detector

> packerid.py

> PEiD

본 게시글은 책 <악성코드 분석 시작하기> 의 내용을 정리한 글 입니다.

 

악성코드 분석 시작하기 : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

1. 정적분석 (Static Analysis)

- 의심스러운 파일을 실행하지 않고 분석하는 기법
> 악성코드 목표 아키텍처 식별하기 : 파일 유형 파악
> 악성코드 식별하기 : 해시 추출
> 백신 엔진을 이용해 의심스러운 바이너리 스캔하기 : 다중 백신 스캐닝
> 파일과 관련된 문자열, 함수, 메타데이터 추출하기 : 문자열 추출
> 분석을 방해하고자 사용한 난독화 기술 식별하기 : 파일 난독화 파악
> 윈도우 실행 파일 정적 분석 : PE 헤더 정보 조사
> 악성코드 샘플들을 비교하고 분류하기 : 퍼지 해싱, 임포트 해시, 섹션 해시, YARA를 이용한 분류

 

1.1 파일 유형 파악

- 바이너리 파일 유형을 구분하는 것은 악성코드의 목표 운영 시스템(윈도우, 리눅스 등)아키텍처(32비트 또는 64비트 플랙폼)를 식별하는데 도움이 됨

 

- 파일 확장자에만 의존하는 것은 권장하지 않음

> 파일 유형의 유일한 식별자가 아님

> 파일 확장자를 수정하거나 숨기는 등의 속임수를 사용

> 파일 시그니처를 이용해 파일 유형 구분

 

- 파일 시그니처 (File Signature) : 파일 헤더에 작성되는 독특한 배열 순서

> 윈도우 실행 파일 또는 PE 파일 (.with, .exe, .dll, .com, .drv, .sys 등)은 파일의 첫 바이트에 MZ 또는 16진수 4D 5A 파일 시그니처를 가짐

 

- 헥사 편집기 (Hex Editor, HxD)로 파일을 열어 파일 시그니처를 찾을 수 있음

- 리눅스에서는 xxd 명령으로 파일 시그니처를, file 유틸리티파일을 식별할 수 있음

- 파이썬 python-magic 모듈을 사용해 파일 유형 구분 가능

1.2 악성코드 식별

- 식별 (Fingerprinting) : 의심스러운 바이너리의 내용을 바탕으로 암호 해시 (Hash) 값을 생성하는 활동

> MD5, SHA1, SHA256 등의 암호 해시 알고리즘은 파일 해시를 생성할 때 사실상 표준

 

- 해시는 악성코드를 식별하는 유일한 식별자

> 파일명 기준으로 악성코드 샘플을 식별하는 것은 효과적이지 않음

> 파일 내용에 기반해 생성된 암호 해시는 식별에 효과적

> 동적 분석 과정에서 악성코드를 실행하면 자신을 다른 위치에 복사하거나 다른 악성코드를 드롭하는 경우가 있음 - 샘플의 암호 해시는 원본 샘플과 동일한지 아닌지를 식별 가능

> 해시를 바이러스토탈 (Virus Total)과 같은 다중 백신 스캐닝 서비스의 데이터베이스를 검색하여 이전에 탐지된 적이 있는지 판단 가능

 

- 리눅스에서는 md5sum. sha256sum, sha1sum 도구를 이용해 파일 해시 생성 가능

- 파이썬 hashlib 모듈을 사용해 해시 생성 가능

1.3 다중 백신 스캐닝

- 의심스러운 바이너리를 다중 백신 스캐너로 스캐닝하면 해당 파일에 악성코드 시그니처가 존재하는지 확인할 수 있음

> 시그니처를 통해 파일과 그 기능에 대한 추가 정보를 제공받을 수 있음

> 분석 시간을 줄여주는 이점

 

- 바이러스토탈 (Virus Total)

> 유명한 웹 기반 악성코드 스캐닝 서비스

> 파일을 업로드하면 다양한 백신 스캐너로 스캐닝하고 실시간 스캔 결과 제공 (일반 사용자일 경우 스캔량 제한 존재)

> 해시, URL, 도메인 또는 IP 주소를 이용해 기존 데이터베이스 검색 가능

> 바이러스토탈 그래프 기능을 활용해 등록한 파일, 도메인, IP 주소, URL과 같은 관련 지표 사이의 관계를 시각화할 수 있음 (로그인 필요)

- Python과 바이러스토탈 API를 활용한 해시 값 질의

> 공개 API를 통해 스크립트를 작성할 수 있는 기능 제공 (API 발급 필요 및 일반 사용자일 경우 스캔량 제한 존재)

> 파일 등록 자동화, 파일/URL 스캔 리포트 검색, 도메인/IP 리포트 검색 제공

 

- 백신 스캐너로 바이너리를 스캔할 때 주의 사항

> 의심 바이너리가 백신 스캐너로 탐지되지 않았다고 해서 안전하다는 것을 의미하는 것은 아님

> 공개 사이트에 바이너리를 업로드하면 등록한 바이너리는 서드파티와 벤더에 공유될 수 있으므로 민감하거나 개인적인 정보가 포함된 경우 스캐닝 서비스에 등록하는 것을 지양

> 공격자들이 검색 기능을 통해 탐지 여부를 확인하여 전략을 변경하거나 탐지를 회피할 수 있음

+ Recent posts