동글동글 라이프
4. NK Record 본문
NK Record 들은 레지스트리 구조를 연결하는데 가장 중요한 역활들을 해주는 Record 입니다.
이 Record는 각 Key 에 따른 하위 목록들의 연결값들을 가지고 있고 최종으로 수정한 시간도 담겨 있습니다.
이 장에서는 NK Record 의 세부적인 값들을 추출하여 나열하는 샘플을 만들어 보겠습니다. 람쥐..^^;
최근 일주일동안 블로그에 열심히 글을 올렸습니다.
오랜만에 강좌를 적어서 그런지 홍보를 안해서 그런지
조회수는 높은데 댓글이 없군요 ㅡ_ㅠ
글을 적는 재미는 쏠쏠합니다~
피드백이 좀 들어왔으면 하는데..
딴지도 환영입니다.( 적당히만 해주시면 ㅡ_ㅠ...)
그러면 시작~~!
Key Records 는 각 Cell 에 대한 사이즈나 정보들을 담고 있습니다.
첫번째 4Byte 는 사이즈가 담겨 있으며, 두번째 값으로는 "nk" 라는 signature를 가지게 됩니다.
여기 nk가 없다면 이건 Key Records가 아닌겁니다!
nk 키로 부터 시작해서 Subkey 를 연결하는 구조로 되어 있기 때문에,
이 정보들을 소중하게 간직하셔야 합니다 :)
이번에는 코드를 먼저 보면서 설명을 드리겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
| 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 |