본문 바로가기
윈도우 포렌식

페이지 파일 pagefile.sys 분석 - 문자열 추출(Python Yara)

by kimnampill 2022. 11. 2.
728x90
반응형

 

윈도우에서 pagefile.sys 는 가상 메모리 파일로 RAM 에서 용량이 모자를 경우 하드디스크의 일부 영역을 메모리 공간으로 활용하는 것이다.

pagefile.sys 파일은 PC를 포맷하지 않고 오래 쓸수록 그 용량이 늘어나며 필자의 pagefile.sys 는 1.5GB 정도로 확인된다.

일반적으로 대용량이기 때문에 텍스트 형태로 보기 어려우며 문자열을 따로 추출해서 봐야한다.

다음 page_brute.py 소스코드를 통해  텍스트 추출을 한다.

#!/usr/bin/python
#
#	page_brute.py
#
import sys
import argparse
import datetime
import glob
import os
import os.path
import binascii

try:
        import yara
except:
        print("[!] - ERROR: Could not import YARA...")
        print("...did you install yara and yara-python? Exiting.")
        sys.exit()


def is_block_null(block):
	#Here we test to see if the block is null..if so, skip.
	RAW_BLOCK=binascii.hexlify(block)
	NULL_REF=binascii.hexlify(NULL_REFERENCE)
	if RAW_BLOCK == NULL_REF:
		return True
	else:
		return False

def build_ruleset():
	if RULETYPE == "FILE":
		try:
			rules=yara.compile(str(RULES))
			print("..... Ruleset Compilation Successful.")
			return rules
		except:
			print("[!] - Could not compile YARA rule: %s") % RULES
			print("Exiting.")
			sys.exit()
	
	elif RULETYPE == "FOLDER":
		RULEDATA=""
		#::Get list of files ending in .yara
		
		RULE_COUNT = len(glob.glob1(RULES,"*.yar"))
		if RULE_COUNT != 0:
			for yara_file in glob.glob(os.path.join(RULES, "*.yar")):
				try:
					yara.compile(str(yara_file))
					print("..... Syntax appears to be OK: %s ") % yara_file
					try:
						with open(yara_file, "r") as sig_file:
							file_contents=sig_file.read()
							RULEDATA=RULEDATA + "\n" + file_contents
					except:
						print("..... SKIPPING: Could not open file for reading: %s ") % yara_file
				except:
					print("..... SKIPPING: Could not compile rule: %s ") % yara_file
			try:
				rules=yara.compile(source=RULEDATA)
				print("..... SUCCESS! Compiled noted yara rulesets.\n")
				return rules
			except:
				print("[!] - Some catastropic error occurred in the compilation of signatureswithin the directory. Exiting.")
				sys.exit()
		else:
			print("No files ending in .yar within: %s ") % RULES
			print("Exiting.")
			sys.exit()
	
	elif RULETYPE == "DEFAULT":
		rules=yara.compile(str(RULES))
		print("[+] - Ruleset Compilation Successful.")
		return rules

	else:
		print("[!] - ERROR: Possible catastrophic error on build_ruleset. Exiting.")
		sys.exit()

def print_procedures():
	print("[+] - PAGE_BRUTE running with the following options:")
	print("\t[-] - FILE: %s") % FILE
	print("\t[-] - PAGE_SIZE: %s") % PAGE_SIZE
	print("\t[-] - RULES TYPE: %s") % RULETYPE
	print("\t[-] - RULE LOCATION: %s") % RULES
	print("\t[-] - INVERSION SCAN: %s") % INVERT
	print("\t[-] - WORKING DIR: %s") % WORKING_DIR
	print("\t=================\n")

def main():

	global FILE
	global PAGE_SIZE
	global RULES
	global SCANNAME
	global INVERT
	global RULETYPE
	global NULL_REFERENCE

	argument_parser = argparse.ArgumentParser(description="Checks pages in pagefiles for YARA-based rule matches. Useful to identify forensic artifacts within Windows-based page files and characterize blocks based on regular expressions.")
	
	group_arg = argument_parser.add_argument_group()
	group_arg.add_argument("-f", "--file", metavar="FILE", help="Pagefile or any chunk/block-based binary file")
	group_arg.add_argument("-p", "--size", metavar="SIZE", help="Size of chunk/block in bytes (Default 4096)")
	group_arg.add_argument("-o", "--scanname", metavar="SCANNAME", help="Descriptor of the scan session - used for output directory")
	group_arg.add_argument("-i", "--invert", help="Given scan options, match all blocks that DO NOT match a ruleset",action='store_true')

	group_arg = argument_parser.add_mutually_exclusive_group()
	group_arg.add_argument("-r", "--rules", metavar="RULEFILE", help="File/directory containing YARA signatures (must end with .yar)")

	args = argument_parser.parse_args()

	if len(sys.argv) < 2:
#		print argument_parser.print_help()
		sys.exit()

	#::Check to see if file was provided::#
	if args.file:
		try:
			with open(args.file):
				FILE=args.file
				print("[+] - PAGE_BRUTE processing file: %s") % FILE
		except:
			print("[!] - Could not open %s. Exiting.") % FILE
			sys.exit()
	else:
		print("[!] - No file provided. Use -f, --file to provide a file. Exiting.")
		sys.exit()

	#::Check to see if page size provided::#
	if args.size:
		PAGE_SIZE=int(args.size)
		NULL_REFERENCE= '\x00' * PAGE_SIZE
	else:
		PAGE_SIZE=4096
		NULL_REFERENCE= '\x00' * PAGE_SIZE

	#::Check if --scan-name provided::#
	if args.scanname:
		SCANNAME=args.scanname
	else:
		SCANNAME="PAGE_BRUTE-" + datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + "-RESULTS"
	
	#::Check if --invert-match provided::#
	if args.invert:
		INVERT=True
	else:
		INVERT=False
	
	#::Check if --rule-file provdided - if not, use default ruleset::#
	if args.rules:
		RULES=args.rules
		try:
			#::Is File?::#
			if os.path.isfile(RULES):
				RULETYPE="FILE"
				print("[+] - YARA rule of File type provided for compilation: %s") % RULES
			elif os.path.isdir(RULES):
				print("[+] - YARA rule of Folder type provided for compilation: %s") % RULES
				RULETYPE="FOLDER"
		except:
			print("[!] - Possible catastrophic error with the provided rule file...exiting.")
			sys.exit()
	else:
		try:
			with open("default_signatures.yar"):
				RULES="default_signatures.yar"
				RULETYPE="DEFAULT"
		except:
			print("[!] - Could not locate \"default_signature.yar\". Find it or provide custom signatures via --rules. Exiting.")
			sys.exit()
	
	#::Compile rules::#
	authoritative_rules=build_ruleset()
	#::Build directory structure
	global WORKING_DIR
	WORKING_DIR=SCANNAME
	if not os.path.exists(WORKING_DIR):
		os.makedirs(WORKING_DIR)
	#::Let People Know what we're doing::#
	print_procedures()	
	#::Find Evil::#
	page_id=0
	with open(FILE, "rb") as page_file:
		while True:
			matched=False
			raw_page=page_file.read(PAGE_SIZE)
			if raw_page == "":
				print("Done!")
				print("Ending page_id is: %s") % page_id
				break
			if not is_block_null(raw_page):
                                #::Determine if block is null...:
				for matches in authoritative_rules.match(data=raw_page):
					if INVERT == True:
						matched=True
					else:
						CHUNK_OUTPUT_DIR=os.path.join(WORKING_DIR,matches.rule)
						print("        [!] FLAGGED BLOCK ") + str(page_id) + ": " + matches.rule

						if not os.path.exists(CHUNK_OUTPUT_DIR):
							os.makedirs(CHUNK_OUTPUT_DIR)

                                       		#::Save chunk to file::#
						CHUNK_OUTPUT_FWD=os.path.join(CHUNK_OUTPUT_DIR,str(page_id) + ".block")
						page_export=open(CHUNK_OUTPUT_FWD,'w+')
						page_export.write(raw_page)
						page_export.close()

				if INVERT == True:
					if matched == False:
						CHUNK_OUTPUT_DIR=os.path.join(WORKING_DIR,"INVERTED-MATCH")
						print("        [!] BLOCK DOES NOT MATCH ANY KNOWN SIGNATURE ") + str(page_id)
						if not os.path.exists(CHUNK_OUTPUT_DIR):
							os.makedirs(CHUNK_OUTPUT_DIR)

						CHUNK_OUTPUT_FWD=os.path.join(CHUNK_OUTPUT_DIR,str(page_id) + ".block")
						page_export=open(CHUNK_OUTPUT_FWD,'w+')
						page_export.write(raw_page)
						page_export.close()
			#::Increment Counter for offset increment::#
			page_id=page_id+1

if __name__ == "__main__":
    main()

 

위 파이썬 소스코드를 실행하려면 YARA 를 추가로 인스톨 해야한다.

CMD 창에서 다음 명령어를 입력하자.

pip install yara

 

위 파이썬 코드에 -h 옵션을 줘서 실행하면 아래와 같다.

 

반응형

댓글