Recent Posts
Recent Comments
03-29 00:00
관리 메뉴

동글동글 라이프

6. Value Data Storage 본문

개발자 이야기/[forensic]Winproof

6. Value Data Storage

동글동글라이프 2012. 4. 10. 20:24

Value Data Storage는  value-list의 offset을 통해 vk record 의 데이터 구성 형태 입니다.

이전 시간에 설명드린 Subkey 를 통해 폴더를 설정했다면 이제 세부적으로 파일들을 채우는 단계라고 생각하시면 됩니다.

이 장에서는 VK record 와 타입별로 데이터를 출력해보는 샘플 코드를 작성해 보겠습니다. 


이제껏 hive 파일을 통해서 레지스트리 값을 복구 시킬때

전체 Key들을 구성하는 부분을 주로 처리 하였습니다.

이제는 각 Key 들에 들어있는 데이터들을 접근해서 직접적으로 뽑아보는 작업을 해보겠습니다.

(아래 그림에서 오른쪽의 값들을 구성해보는 겁니다 ㅋㅋ)


각각의 레지스트리 키들은 여러개의 Value들을 가질 수 있습니다. 

이런 Value list 들은 subkey 에서 'ri' 같은 Signature를 가지고 있지 않고 

각 Value 들이 위치하는 주소의 값들만 들어 있습니다.

nk Record 에서 Value-List 를 가르키는 주소를 통해 이 Value-List 의 주소를 알아 낼 수 있습니다.


# value 리스트의 사이즈를 만큼의 주소를 @ofs_val배열하여 반환한다.

sub readValList {
	my ($offset,$num_vals) = (@_);
	my $record;
	my $bytes_to_read = $num_vals * 4;
	seek($reg,$offset,0);
	my $bytes = read($reg,$record,$bytes_to_read);
	if ($bytes == $bytes_to_read) {
		my @ofs_val = unpack("L*",$record);
		if (scalar(@ofs_val) == $num_vals) {
			return @ofs_val;
		}
		else {
			print "readValList bytes read error: ".$bytes;
			return;
		}
	}
}

처음 Value 값을 뽑을 때 Value 값이 하나일지라도 무조건 Value List 를 거쳐야 합니다.

Signature 가 없기 때문에 그렇기도 한 부분이죠 그래서 혹시나 Value List 를 잘 못 읽었을 때에 대한

예외처리도 되어 있는 것을 확인 할 수 있습니다.


Value Record는 'vk'의 Signature 를 가진 구조로 되어 있습니다. 

위에 표를 확인해보면 Value의 이름, 데이터들이 들어 있습니다.

레지스트리에 Value의 값이 없을 경우에는 Name value의 값이 0으로 설정되게 되고

Windows Registry Edtors(레지스트리 편집기) 에서는 Default(기본값) 으로 설정되게 됩니다.

0x000b 에서 Data type이 있는데 이 데이터 타입에 따라 레지스트리 편집기에서 보여줄 때 

문자형태, 숫자, 이진 데이터 등으로  보여줍니다.

다른 타입들은 전부 이진 또는 16진수로 보여지게 됩니다.




Value type 들은 코드상에서 아래와 같이 정의되어 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
#registry Value Type
my %regtypes = (0 => "REG_NONE",
			1  => "REG_SZ",
	                2  => "REG_EXPAND_SZ",
	                3  => "REG_BINARY",
	                4  => "REG_DWORD",
	                5  => "REG_DWORD_BIG_ENDIAN",
	                6  => "REG_LINK",
	                7  => "REG_MULTI_SZ",
	                8  => "REG_RESOURCE_LIST",
	                9  => "REG_FULL_RESOURCE_DESCRIPTOR",
	                10 => "REG_RESOURCE_REQUIREMENTS_LIST");

이제 regtypes 들을 통해 아래와 같이 나열이 가능합니다.

hash로 정리해두면 바로 해당값들의 문자열을 쉽게 뽑아내는게 가능하죠 :)

1
2
3
4
5
6
7
8
if ($nk{no_values} > 0) { 
	my @ofs_vallist = readValList(($nk{ofs_vallist} + $ADJUST),$nk{no_values});
	foreach my $i (0..(scalar(@ofs_vallist) - 1)) {
		my %vk = readVkRecord($ofs_vallist[$i] + $ADJUST);		
		print "\t--> ".$vk{valname}.";".$regtypes{$vk{val_type}}.";".$vk{data}."\n";
	}
	print "\n";
}

readVKRecord를 호출하여 해시값을 뽑아내는 함수는 다음과 같습니다.


# Value Record 의 이름 및 값들을 형식에 따라 읽어온 뒤 해시 값으로 반환

sub readVkRecord {
	my $offset = shift;
	my $record;
	my %vk = ();
	seek($reg,$offset,0);
	my $bytes = read($reg,$record,20);
	if ($bytes == 20) {

		my (@recs)    = unpack("SSLLLSS",$record);
		$vk{val_type} = 0;
		$vk{id}       = $recs[0];
		$vk{len_name} = $recs[1];
		$vk{len_data} = $recs[2];
		$vk{ofs_data} = $recs[3];
		$vk{val_type} = $recs[4];
		$vk{flag}     = $recs[5];
		# value 의 이름을 읽어온다.
		if ( $vk{len_name} == 0) {
			$vk{valname} = "Default";
			#vk{val_type} 값이 잘 못된 값이 들어가기도 한다. 
			$vk{val_type} = 1; # REG_SZ 로 설정
			$vk{data} = 0;
			return %vk;
		}
		else {
			seek($reg,$offset + 20,0);
			read($reg,$record,$vk{len_name});
			$vk{valname}  = $record;
		}
		
		# 각 valuetype에 따라 데이터를 출력하는 방법을 다르게 하기 위한 작업
		if ($vk{len_data} & 0x80000000 || $vk{val_type} == 4) {
			# $vk{len_data}가 0x80000000거나 REG_DWORD 일 경우 아래 형식으로 저장
			$vk{data} = $vk{ofs_data};			
			$vk{data} = "" if ($vk{val_type} == 7);
			$vk{data} = chr($vk{data}) if ($vk{val_type} == 1);
		}
		else {
			# REG_SZ , REG_EXPAND_SZ , REG_MULTI_SZ 일 시에 ASCII 값으로 변환
			$vk{data} = _getValueData($vk{ofs_data} + $ADJUST,$vk{len_data});
			$vk{data} = _uniToAscii($vk{data}) if ($vk{val_type} == 1 ||
			                                       $vk{val_type} == 2 ||
			                                       $vk{val_type} == 7);
		}
		# REG_BINARY , REG_RESOURCE_LIST , REG_RESOURCE_REQUIREMENTS_LIST 일 시 이진값으로 변환
		$vk{data} = _translateBinary( $vk{data}) if ($vk{val_type} == 3 || 
									$vk{val_type} == 8 ||
									$vk{val_type} == 10);			
		return %vk;
	}
	else {
		print "readVkRecord bytes read error: ".$bytes;
		return;
	}
}

# 넘겨받은 길이 만큼 데이터를 읽어 값을 반환

sub _getValueData {
	my ($offset,$len ) = (@_);
	my $record;
	seek($reg,$offset,0);
	my $bytes = read($reg,$record,$len);
	if ($bytes == $len) {
		return $record;
	}
	else {
		print "_getValData error: $bytes of $len bytes read.";
		return;
	}
}

# 유니코드 문자열을 ASCII로 변환

sub _uniToAscii {
	my $str = shift;
	my $len = length($str);
	my $newlen = $len - 1;
	my @str2;
	my @str1 = split(//,$str,$len);
	foreach my $i (0..($len - 1)) {
		if ($i % 2) {
			# 아스키코드 일경우에는 홀수값이 \00이라 값을 읽을 필요가 없다.
		}
		else {
			push(@str2,$str1[$i]);
		}
	}	
	return join('',@str2);
}

# 데이터를 2진 값으로 변환하여 출력

sub _translateBinary {
	my $str = unpack("H*",$_[0]);	
	my $len = length($str);
	my @nstr = split(//,$str,$len);
	my @list;
	foreach (0..($len/2)-1) { # 기존소스에는 -1이 없어 warning 발생
		push(@list,$nstr[$_*2].$nstr[($_*2)+1]);
	}
	return join(' ',@list);
}

휴.. 소스코드 안의 주석으로 설명을 했습니다!!

원본 소스코드에서 2군데에서 warning이 발생하는 것을 확인했는데 어딘지 찾아보던 중

$vk{val_type} 값이 초기화 되어 있지 않을 경우에 값이 들어가지 않아 생기는 warning 과

_translateBinary 코드에서 배열의 크기보다 더 초과하게 foreach 문을 돌리는 부분을 수정하니

warning 이 사라지더군요 :)


결과값을 확인할 시간입니다.. 두구두구두구...

>perl rega.pl SAM

\SAM

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

\SAM\SAM

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

        --> C;REG_BINARY;07 00 01 00 00 00 00 00 98 00 00 00 02 00 01 00 01 00 1

4 80 78 00 00 00 88 00 00 00 14 00 00 00 44 00 00 00 02 00 30 00 02 00 00 00 02

c0 14 00 0e 00 05 01 01 01 00 00 00 00 00 01 00 00 00 00 02 c0 14 00 ff ff 1f 00

 01 01 00 00 00 00 00 05 07 00 00 00 02 00 34 00 02 00 00 00 00 00 14 00 31 00 0

2 00 01 01 00 00 00 00 00 01 00 00 00 00 00 00 18 00 3f 00 0f 00 01 02 00 00 00

00 00 05 20 00 00 00 20 02 00 00 01 02 00 00 00 00 00 05 20 00 00 00 20 02 00 00

 01 02 00 00 00 00 00 05 20 00 00 00 20 02 00 00

... 중략

 00 00 00 05 20 00 00 00 20 02 00 00 00 00 24 00 44 00 02 00 01 05 00 00 00 00 0

0 05 15 00 00 00 fd 37 42 40 9b 0b c1 1e 23 5f 63 6b f4 01 00 00 01 02 00 00 00

00 00 05 20 00 00 00 20 02 00 00 01 02 00 00 00 00 00 05 20 00 00 00 20 02 00 00

 41 00 64 00 6d 00 69 00 6e 00 69 00 73 00 74 00 72 00 61 00 74 00 6f 00 72 00 0

0 00 f4 ce e8 d4 30 d1 2f 00 c4 b3 54 ba 78 c7 44 c7 20 00 00 ad ac b9 58 d5 c4

b3 5d b8 20 00 30 ae f8 bc 20 00 1c c8 f5 ac 1c b4 20 00 c4 ac 15 c8 ff ff ff ff

 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff fd 97 40 01 02 00 00 07 00 0

0 00 01 00 01 00 01 00 01 00 d4 71 1f d6 c5 fd 97 40 52 1f eb a9 a1 ea e9 4b 01

00 01 00 01 00 01 00


\SAM\SAM\RXACT

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

        --> Default;REG_SZ;0


이것으로 레지스트리의 타입에 따라 데이터들의 형식이 다르게 출력 되는 것을 확인 할 수 있습니다!!!

Value Key 까지 구성했다면 분석을 위한 데이터를 뽑는부분 까지는 모두 완료 되었습니다.

다음장에는 Registry 의 중요한 값들을 구성해보는 코드를 작성해 보겠습니다.



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

8. Registry File Parser Source code  (6) 2012.04.12
7. UserAssist Analysis ( ROT13 )  (1) 2012.04.11
5. SubKey List  (1) 2012.04.09
4. NK Record  (0) 2012.04.06
3. Bin Header  (2) 2012.04.05
Comments