Recent Posts
Recent Comments
03-19 12:42
관리 메뉴

동글동글 라이프

4. NK Record 본문

개발자 이야기/[forensic]Winproof

4. NK Record

동글동글라이프 2012. 4. 6. 23:56

NK Record 들은 레지스트리 구조를 연결하는데 가장 중요한 역활들을 해주는 Record 입니다.

이 Record는 각 Key 에 따른 하위 목록들의 연결값들을 가지고 있고 최종으로 수정한 시간도 담겨 있습니다.

이 장에서는 NK Record 의 세부적인 값들을 추출하여 나열하는 샘플을 만들어 보겠습니다. 람쥐..^^;

최근 일주일동안 블로그에 열심히 글을 올렸습니다. 

오랜만에 강좌를 적어서 그런지 홍보를 안해서 그런지

조회수는 높은데 댓글이 없군요 ㅡ_ㅠ


글을 적는 재미는 쏠쏠합니다~

피드백이 좀 들어왔으면 하는데.. 

딴지도 환영입니다.( 적당히만 해주시면 ㅡ_ㅠ...)


그러면 시작~~!


Key Records 는 각 Cell 에 대한 사이즈나 정보들을 담고 있습니다. 

첫번째 4Byte 는 사이즈가 담겨 있으며, 두번째 값으로는 "nk" 라는 signature를 가지게 됩니다.

여기 nk가 없다면 이건 Key Records가 아닌겁니다!

nk 키로 부터 시작해서 Subkey 를 연결하는 구조로 되어 있기 때문에,

이 정보들을 소중하게 간직하셔야 합니다 :)


이번에는 코드를 먼저 보면서 설명을 드리겠습니다.


use strict;
use warnings;

my $ADJUST  = 0x1004;	
my $file = shift || die "You must enter a filename.\n";
open my $reg, '<', $file  or die "cannot open [$file] file: $!";

my ($node,$offset) = locateRecord($ADJUST);
my $nt = _getNodeType($offset); 

if ($nt == 0x6b6e) { # 'nk' record 일 경우 파싱을 시작 합니다.
	parseNkRecords("",$offset);
}
else {
	printf "Node not an nk node: 0x%x\n",$nt;
	die;
}
close $reg; 


# nk 의 root key 값을 찾는 코드

sub locateRecord {
	my $offset = shift;
	my $record;
	seek($reg,$offset,0);
	while(1) {
		read($reg,$record,4);
		my ($tag,$id) = unpack("SS",$record);
		if ($tag == 0x6b6e && $id == 0x2c) { 
			# print "nk record located.\n";
			return("nk",$offset);
		}
		$offset = $offset + 2;
		seek($reg,$offset,0);
	}
}


# offset으로부터 2byte를 읽어와 unpack 을 사용하여 node의 ID를 얻는 방법입니다.
# 이 함수를 하나 만들어 두면 앞으로 각 Record 의 타입을 확인하기 편하죠!

sub _getNodeType {
	my $offset = $_[0];
	my $record;
	seek($reg,$offset,0);
	my $bytes = read($reg,$record,2);
	if ($bytes == 2) {
		return unpack("S",$record);
	}
	else {
		print "_getNodeType error - only $bytes read.";
		return;
	}
}

# nk record 들을 파싱하는 부분입니다. 
# 파싱한 후 nk 의 값들을 traverse 하는 코드들은 다음장에서 :)

sub parseNkRecords {
	my ($name, $offset) = (@_);
	my %nk = readNkRecord($offset);
	#nk record 의 정보들을 출력합니다. 설명까지 앞에 다 붙였습니다! 
	print $name."\\".$nk{keyname}."\n"; 
	print "LastWrite time: ".gmtime(getTime($nk{time1},$nk{time2}))."\n";
	print "Number of subkeys : ".$nk{no_subkeys}."\n";
	print "Pointer to the subkey-list : ".$nk{ofs_lf}."\n";
	print "Number of values : ".$nk{no_values}."\n";
	print "Pointer to the value-list for values : ".$nk{ofs_vallist}."\n";
	print "Pointer to the SK record : ".$nk{ofs_sk}."\n";
	print "Pointer to the class name : ".$nk{ofs_classname}."\n";
	print "Key name length : ".$nk{len_name}."\n";
	print "Class name length : ".$nk{len_classname}."\n";
	# 이후에 Key들을 연결해줄 코드들이 쭈욱~~ 붙게 됩니다.
}


# 위의 표에서 본 값들을 전부 읽어와서 각각의 변수들로 저장시킵니다.
# perl 의 hash를 통해서 하나하나 이름을 붙여 깔끔하게 저장하네요 :)

sub readNkRecord {
	my $offset = shift;
	my $record;
	my %nk = ();
	seek($reg,$offset,0);
	my $bytes = read($reg,$record,76);
	if ($bytes == 76) {
		# unpack의 알흠다운 활용법입니다.
		my (@recs)	= unpack("SSL3LLLLLLLLLL4LSS",$record);
		$nk{id}		= $recs[0];
		$nk{type}		= $recs[1];
		$nk{time1}	= $recs[2];
		$nk{time2}	= $recs[3];
 		$nk{time3}	= $recs[4];
		$nk{no_subkeys}	= $recs[6];
		$nk{ofs_lf}		= $recs[8];
		$nk{no_values}	= $recs[10];
		$nk{ofs_vallist}	= $recs[11];
		$nk{ofs_sk}	= $recs[12];
		$nk{ofs_classname}	= $recs[13];
		$nk{len_name}	= $recs[19];
		$nk{len_classname}	= $recs[20];

		# key의 이름을 알아내어 저장합니다		
		seek($reg,$offset + 76,0);
		read($reg,$record,$nk{len_name});
		$nk{keyname}       = $record;

		# 여기까지 읽은 전체 바이트의 수는 ($num_bytes + $nk_rec{len_name}) 입니다.
		# 다시 말하면 nk의 76byte + nk의 keyname 길이만큼 입니다.
		# 이 값들을 활용하기 위해 hash 값을 return 시킵니다.
		return %nk;
	}
	else {
		print "readNkRecord bytes read error: ".$bytes;
		return;
	}
}


# 2개의 4Byte time값을 64-bit NT timestamp 값으로 변환해주는 코드입니다.

sub getTime() {
	my $lo = shift;
	my $hi = shift;
	my $t;

	if ($lo == 0 && $hi == 0) {
		$t = 0;
	} else {
		$lo -= 0xd53e8000;
		$hi -= 0x019db1de;
		$t = int($hi*429.4967296 + $lo/1e7);
	};
	$t = 0 if ($t < 0);
	return $t;
}

!!! 

코드가 엄청 늘어 났습니다. 

나중에 각각의 키들의 offset 에 따라서 재귀적으로 함수를 호출해야 하기 

때문에 코드를 함수로 빼는 작업이 중요합니다.

위의 코드를 설명을 드리자면 

가장 처음의 nk record 를 접근해서 record의 값을 추출한 뒤 해시에 저장한 후에

그 값 들을 출력해주고 있습니다.


코드 실행의 결과는 아래와 같습니다.

>perl rega.pl SAM

\SAM

LastWrite time: Sun Jul  4 09:33:18 2010

Number of subkeys : 1

Pointer to the subkey-list : 504

Number of values : 0

Pointer to the value-list for values : 4294967295

Pointer to the SK record : 120

Pointer to the class name : 4294967295

Key name length : 3

Class name length : 0

음?? value-lis의 offset의 값들이 말도 안되는 숫자가 나오는 이유는

values가 0개기 때문입니다. Class name의 offset 도 마찬가지구요 :)


forensic 관점에서 연결해주는 offset 이나 각 number 들이 중요하진 않습니다.

여기서 빨간색으로 표시한 key 이름과 LastWrite의 시간이 가장 중요하죠.

나중에 분석을 할 때나 UI 도구을 만들때도 이 데이터들이 중요한 역활을 합니다.


그럼 오늘 강좌는 여기까지 하고.. 

불타는 금요일 + 주말  보내세요~~ :)




'개발자 이야기 > [forensic]Winproof' 카테고리의 다른 글

6. Value Data Storage  (1) 2012.04.10
5. SubKey List  (1) 2012.04.09
3. Bin Header  (2) 2012.04.05
2. Registry hive structure  (1) 2012.04.04
1.시작하며...  (3) 2012.04.03
Comments