[ntfs forensic 강좌] 1. ntfs 기본 (file, file record, inode, attribute)
2013/06/03 - [Research/Forensic & NTFS] - ntfs parser (ntfs parsing, analysis, c, c++) (ntfs 파싱, 분석, VC++ 9.0) 와 연관있습니다. 해당 GPL 라이센스를 따르는 Open Source와 ntfs에 대해 분석한 문서를 공유합니다.
우선, 본 문서는,
- 이론 + 적용, 즉, ntfs의 이론 뿐 아니라, 위 GPL 소스와 함께 실무적인 부분도 다룬다.
- 본 블로그 저자 또한 아직 ntfs를 계속 연구하고 있는 중이라, 잘못된 부분이 있을 수 있으며, 향후 기능과 내용이 보강될 것이다.
- 우선, ntfs의 다른 document들도 함께 사전 습득한 상태면 더욱 이해가 빠를 것이다.
와 같이 정리될 것입니다.
ntfs 분석과 그에 따른 실무(parsing, forensic, ...)만 해도 책한권 분량의 방대한 내용이 나올법하지만, 본 포스트와 연관된 시리즈들은 최대한 짧고 간결히 정리하고자 합니다.
그러기 위해선 우선,
- a.txt (disk seek pos : 1024, size : 5 sectors), b.txt(disk seek pos : 2048, size : 10 sectors), ...
와 같은 file allocation table 정보 - 볼륨 이름
- 사용되지 않는 sector의 정보
- 실제 a.txt와 b.txt의 내용이 저장된 data 영역
- ...
와 같은 정보가 최소 필요할 것입니다. ntfs에서는 이러한 정보들을 file이라고 합니다. 즉, file allocation table 정보, 사용되지 않는 sector의 정보, 볼륨 이름, 사용되지 않는 sector의 정보등이 file로 저장됩니다. 물론, 논리적인 a.txt, b.txt도 당연히 file로 저장됩니다. 즉, 일반적인 file system에서 사용되는 논리적인 file과 함께 위 항목들도 동일한 방식으로 file에 저장되어 사용되고 있습니다. 다시 말해, 그 정보들은 ntfs에서 file 영역과 구별된 다른 영역에서 만들어 관리하는 것이 아니라, 그 자체도 file로 만들어 일반적인 file 처리 논리를 따르도록 한 것입니다.
즉, ntfs에게 file이란, "ntfs file system의 논리적 정보 단위"가 됩니다. 물론, 물리적 정보단위는 cluster가 될 것 같구요. 그리고 이러한 정보 단위는 최소 한개 이상의 attribute를 가지고 있습니다.
어떻든, 이러한 ntfs의 file은, 일반적인 사용자 혹은 OS에서 사용되는 file(normal file)과 앞선 목록들과 같은 ntfs 파일 시스템 내부에서 사용될 만한 file(meta-data file)로 구별됩니다. 관련해서는, IsNormalFile(...) 함수를 참고하십시요.
[fig. 01]
즉, a.txt는 총 10byte에 1234567890, b.txt는 4byte에 abcd, 그리고 c.txt는 6byte에 qwerty라는 정보를 가진다고 가정합시다. 완벽한 file system으로 보일지 모르나, file 삭제나, file indexing(예, c.txt를 찾아가기), directory 구조, file system의 업그레이드로 인한 포맷 변경등등을 생각해보면 완벽이 아닌, 가장 바보같은 file system일 것입니다.이런 경우에 대비하여, 보통 s/w 개발에서는 page 같은 개념이 응용되곤 합니다. 즉, 하나의 정보 단위를 동일 크기의 data에 담도록 하는 것입니다. 이는 다음과 같이 표현될 것입니다.
[fig. 02]
즉, 10MB를 일정 크기의 page 단위로 논리적 분리를 수행한 다음 1, 2, 3, ... 과 같은 일렬 번호를 부여합니다. 그리고, 1에 a.txt, 4에 b.txt, 그리고 6에 c.txt에 해당되는 정보와 data를 저장합니다. 이렇게 되면, file 삭제가 용이해 보이네요. 즉, 예를 들어, 4번 page 정보를 clear하면 되니깐요. 그리고, c.txt를 찾을 때에도, 1번에서 6번 page를 탐색하면 찾을 수 있습니다. [fig. 01]에서 만약 b.txt 정보를 overwrite하다 오류가 발생하면 그 이후의 정보 탐색이 불가능해 질 수도 있지만, [fig. 02]에서는 오류 처리 또한 상당히 간편해 집니다. 그리고, c.txt 탐색시에도 앞선 a.txt나 b.txt의 내용과 독립적으로 순차적으로만 검색하면 손쉽게 찾을 수 있습니다.
이와 같이 ntfs에서도 [fig. 02]와 같은 논리로 file이 저장됩니다. 단지, 차이점이라곤 page라는 용어가 file record로 사용된다는것 뿐입니다. 그리고 1, 2, 3, ... 과 같은 일렬 번호는 inode라고 정의하여 사용합니다. (일반적인 ntfs document에서는 inode 이외의 다른 용어가 사용되기도 합니다.)
물론, [fig. 01]과 비교하여 disk 사용 효율은 떨어지게 됩니다. 왜냐 하면 아래와 같은 형태로 저장될 것이기 때문입니다.
[fig. 03]
위 그림은 [fig. 02]의 1번 inode를 확대한 것입니다. 특이한건 필요한 정보가 저장되었음에도 불구하고, 뒷쪽 회색 부분의 불필요한 공간이 남아있다는 뜻입니다. 이러한 공간을 보통 slack 영역이라고 불리기도 합니다. 여하튼, 효율은 [fig. 02]가 [fig. 01]보다 떨어지지만, 그 효과가 크기 때문에, ntfs는 [fig. 02]를 사용하였다고 보면 됩니다. 물론, 최근의 웬만한 file system은 [fig. 02]를 근간으로 하고 있습니다.
여기에서 "그럼 File record 크기를 넘어가는 정보가 필요한 file은 어떻게 저장되는가?" 질문을 받을 수 있습니다.
ntfs에서는 이런 경우, 다른 비어있는 file record에 나누어 저장하는 방식을 따르고 있습니다. 즉, file은 상황에 따라 disk상에서 연속되지 않게 저장될 수 있다는 것입니다. 연속되지 않기 때문에, linked list와 유사한 개념인 run list라는 값이 함께 저장되며, 이는 다음에 설명하도록 합니다. 참고로 ntfs에서 해당 크기는 1024 byte로 되어 있습니다.
이제, 마지막으로 inode를 다음과 같이 정의합니다 - 임의의 file이 저장되어 있는 file record 번호
이와 같이 inode는 다음과 같은 종류로 분류됩니다. (typedef UBYTE8 TYPE_INODE를 참고)
inode 번호 | inode 이름 |
0 | TYPE_INODE_MFT |
1 | TYPE_INODE_MFTMIRR |
2 | TYPE_INODE_LOGFILE |
3 | TYPE_INODE_VOLUME |
4 | TYPE_INODE_ATTRDEF |
5 | TYPE_INODE_DOT |
6 | TYPE_INODE_BITMAP |
7 | TYPE_INODE_BOOT |
8 | TYPE_INODE_BADCLUS |
9 | TYPE_INODE_QUOTA |
10 | TYPE_INODE_UPCASE |
11 | TYPE_INODE_EXTEND |
24 | TYPE_INODE_FILE |
24 | TYPE_INODE_DIR |
[fig. 04]
즉, C:\Windows\notepad.exe는 임의의 inode에 위치하고 있을 텐데, 우선, 크기가 175KB로 1024byte보다 큽니다. 그러므로, data는 다른 file record에 위치하고 있을 것입니다. 그리고, 크기, 시간, 속성, 보안등등 하나의 file에 data말고 여러 속성들이 포함되어 있습니다. 이를 atribute라고 하며, 하나의 file에는 여러개의 attribute가 포함되어 있습니다. 가장 대표적이며 반드시 포함되는 것이 이름 attribute, 즉, $FILE_NAME attribute값 입니다(통상적으로 attribute는 $로 시작하고, 모두 대문자로 표현합니다).
앞선 [fig. 03]를 보십시요. 해당 file은 총 2개의 attribute를 가지고 있는데, $FILE_NAME과 $DATA로 구성되어 있습니다. normal file인 경우, 다음과 같은 속성이 포함됩니다.
- $STANDARD_INFORMATION
; 기본적인 파일 속성. 위 [fig. 04]에 해당되는 값들이라고 가정하라. - $FILE_NAME
; 파일 이름 속성. 위 [fig. 04]에 해당되는 값들도 함께 포함되어 있다. - $SECURITY_DESCRIPTOR
; ACL(access control list)에 해당되는 값. 위 [fig. 04]의 보안탭에 해당되는 값이 저장되어 있다. - $DATA (해당 inode가 file인 경우)
; 파일의 실제 저장된 data - $INDEX_ROOT (해당 inode가 directory인 경우)
; sub directory를 나열하기 위한 b-tree의 root index entry 값 - $INDEX_ALLOCATION (해당 inode가 directory인 경우)
; b-tree의 sub node들이 저장되어 있는 index entry 값 - $BITMAP (해당 inode가 directory인 경우)
; sub directory에 있는 index entry를 나열하기 위한 index entry 값들이 저장된 Lcn 번호
그리고, meta-data file인 경우, 그 조합은 다양한데, 예를 들어, TYPE_INODE_VOLUME에서는 $VOLUME_NAME 속성값이 저장되어 있습니다.
코드상으로는, typedef UBYTE4 TYPE_ATTRIB를 참고하시기 바랍니다.
#define TYPE_ATTRIB_UNKNOWN 0
#define TYPE_ATTRIB_STANDARD_INFORMATION 16
#define TYPE_ATTRIB_ATTRIBUTE_LIST 32
#define TYPE_ATTRIB_FILE_NAME 48
#define TYPE_ATTRIB_VOLUME_VERSION 64 // ntfs 1.2 only
#define TYPE_ATTRIB_OBJECT_ID 64 // ntfs 3.0 over
#define TYPE_ATTRIB_SECURITY_DESCRIPTOR 80
#define TYPE_ATTRIB_VOLUME_NAME 96
#define TYPE_ATTRIB_VOLUME_INFORMATION 112
#define TYPE_ATTRIB_DATA 128
#define TYPE_ATTRIB_INDEX_ROOT 144
#define TYPE_ATTRIB_INDEX_ALLOCATION 160
#define TYPE_ATTRIB_BITMAP 176
#define TYPE_ATTRIB_SYMBOLIC_LINK 192 // ntfs 1.2 only
#define TYPE_ATTRIB_REPARSE_POINT 192 // ntfs 3.0 over
#define TYPE_ATTRIB_EA_INFORMATION 208 // os/2
#define TYPE_ATTRIB_EA 224 // os/2
#define TYPE_LOGGED_UTILITY_STREAM 256 // ntfs 3.0 over
2장이후는,
- ntfs layout (meta-data inode, $MFT)
- lcn / vcn 개념
- run list
- inode의 attribute 구하기 (fixup)
- $BITMAP을 통한 사용중 cluster 나열하기
- index (b-tree) traverse (sequence 처리, b-tree 탐색, vcn의 숨은 함정)
- ...
등을 진행할 예정입니다.