## What is MFT Attribute? NTFS 파일시스템에서는 모든 파일(디렉토리)을 하나의 엔트리의 형태로 메타데이터만 저장해 놓는다. 이와 비슷한 내용을 [[$MFT]] 에서 다루었었다. 잘 기억이 나지 않는다면 복습하고 오는 것을 추천한다. MFT Attribute를 한 문장으로 표현하자면 하나의 파일 안에 있는 다양한 속성값들에 대한 리스트다. 이때 말하는 속성은 파일의 시간값과 같은 메타데이터나, 실질적으로 파일의 내용을 담당하는 데이터를 포함하게 된다. 분량이 사실 방대한 편이기 때문에 2개의 게시글에 걸쳐 다룰 예정이다. 이번 게시글은 첫번째 게시글이다. 주로 header와 부수적인 기능들에 대해서 알아볼 예정이다. ## Why MFT Attribute is important? 파일 내 다양한 속성 (데이터, 메타데이터, 등등)에 접근하기 위해서는 Attribute라 불리는 속성값을 통해 접근해야 한다. Attribute 구조를 분석하여 직접 (메타)데이터를 얻어내거나, 구조체 내에 명시된 오프셋을 통해 파일의 (메타)데이터에 접근할 수 있다. 실질적으로 파일의 모든 정보가 저장되는 곳이고, 파일의 데이터에 접근하기 위한 첫 발자국이므로 MFT attribute를 분석하는 것은 매우 중요하다. 데이터랑 시간값을 비롯한 단순하지만 중요한 정보 이외에도 부수적으로 많은 정보가 같이 저장되기 때문에 **어떤 종류**의 데이터가 **어디에** 저장되는지 파악하는 것이 이번 글의 목표이다. 이를 달성하기 위해서는 그 구조를 완벽히 파악하고, 각각의 구조체들은 어떤 관계를 가지는지 알아볼 필요가 있겠다. 따라서 이번 글에서는 다음 내용을 중점적으로 알아볼 것이다. 1. 모든 Attribute의 공통적으로 사용되는 구조는 어떻게 구성되어 있는지 2. Attriubute 별로 어떤 정보가 저장되는지 3. Attribute 구조를 이해함으로써 얻을 수 있는 정보는 무엇인지 ## Structure of MFT Attribute 먼저 하나의 MFT Entry는 Attribute Header와 Attribute Content로 구성되어 있다. 파일시스템 부분에서 끊임없이 다루겠지만 어떤 구조체의 header라는 것은 범용적으로 사용되는 구조체로써, 특정 정보에 대한 위치 정보나 길이 등 Content로의 접근 방법이나 기타 부수적인 정보를 다루는 구조체이다. ### Attribute Header MFT Entry에 대해서 설명할 때 Entry 사이즈 자체가 너무 커지게 된다면 여러 Entry를 사용하는 Base-Entry와 Non-Base-Entry를 사용하는 방식을 택했다. Attribute역시 그 크기가 커지게 된다면 Entry 사이즈가 커지게 되므로 동일한 문제를 야기한다. 그렇다면 Attribute의 크기가 커짐에 따라 Entry 크기가 커지면 어떤 식으로 파일을 관리할까? 정답은 바로 Non-Base-Entry를 사용하는 것이 아닌, 다른 위치에 Attribute Content를 사용하는 Non-Resident Attribute 방식을 사용하게 된다. 그렇다면 지정된 크기 이하의 Attribute는 Resident Attribute 방식을 이용해 저장된다. > [!Question] **Why Resident & Non-Resident?** > Resident 라는 이름 자체는 Entry 내에 Attribute가 거주(Reside) 하고 있다는 의미로 해석하면 이해가 편하다. > Non-Resident의 경우 Entry 내에 Attribute가 거주 하지 않기(Not Resident)하기 때문에 이와 같은 이름이 선택되었다. > 이런 분류 속에서도 공통적으로 다뤄지는 영역이 존재하고, 이를 Common Header라 한다. 정리하자면, Attribute의 header는 `Common + Resident` 혹은 `Common + Non-Resident`의 조합으로 구성된다. 각각의 Header에 대해서 알아보도록 하자. ### **Common Header** 앞서도 간략히 설명했지만, Common Header는 Resident 와 Non-Resident 헤더에 공통적으로 포함되는 영역이다. 해당 영역에서 다음 Header가 Resident인지 Non-Resident인지 표시하며, 이 Attribute가 어떤 Attribute인지도 설명한다. 자세한 구조에 대해서 알아보자. ![[%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-05-02_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.28.12.png]] |Name|Offset|Size(byte)|Description / Value| |---|---|---|---| |Attribute Type ID|0x00|4|Attribute를 식별할 수 있는 고유 Type ID| |Attribute Length|0x04|4|Attribute 전체 길이 (Header 길이 포함)| |Non-Resident Flag|0x08|1|Non-Resident 인 경우 1로 표시, Resident 인 경우 0으로 표시| |Name Length|0x09|1|Attribute 이름의 길이(지정되지 않은 경우 0)| |Name Offset|0x0A|2|Attribute에 이름이 지정된 경우, 이름의 위치| |Flags|0x0C|2|Attribute의 상태 <br>- 0x0001 : 압축 <br>- 0x4000 : 암호화 <br>- 0x8000 : Sparse| |Attribute ID|0x0E|2|Entry 내에서 Attribute의 고유 ID| > [!NOTE] **Flags** > - 압축 : $DATA 속성에서 주로 사용되며, Non-Resident Attribute의 형식으로 사용될 때 압축이 가능하다. > 압축 단위로 클러스터를 분할하여 압축하며, 이때 변형된 LZ77 알고리즘을 사용한다. > > - 암호화 : EFS가 File Encyption Key(FEK)를 생성하여 대칭키 방식으로 파일을 암호화. > 사용하는 암호화 알고리즘은 운영체제 버전에 따라 상이하다. > > - Sparse : NTFS 5.0부터 추가된 속성으로 $DATA attribute 에서만 사용된다. > 파일의 크기는 존재하나 대부분의 데이터가 Null Byte(0x00)인 경우 실제 크기만 기록하고 Cluster를 할당하지는 않는 저장 정책 > [!Question] **Attribute Type ID vs Attribute ID?** > 서로 이름이 비슷하여 동일한 기능을 하는 요소라고 착각할 여지가 있다. > 하지만 Attribute Type ID는 모든 Entry를 초월하여 범용적으로 사용되는 값인데 반해, Attributer ID는 하나의 Entry 내에서 각 Attribute를 구분하기 위해 할당되는 2바이트 값이다. > > 이 뜻은 하나의 엔트리 내에 같은 속성이 여러개 할당될 수도 있다는 것을 암시한다 > [[ADS(Alternative Data Stream)]] 가 하나의 예시가 되겠다. > ### Resident Header ![[%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-05-02_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_10.53.18.png]] |Name|Offset|Size(byte)|Description / Value| |---|---|---|---| |Size of Content|0x00|4|Attribute 내용의 길이| |Offset to Content|0x04|2|속성의 시작. <br>이름이 있는 Attribute의 경우, 이름으로 시작, 이름이 없는 Attribute의 경우 Content로 바로 시작| |Index Flag|0x06|1|검색을 위해 사용되는 index 정보. <br>설정되어 있는 경우 시스템이 해당 엔트리를 검색에 활용한다. <br>이 경우 전체 디렉토리 엔트리를 통해 파일을 검색하지 않아도 되므로 빠른 접근이 가능하다.| |Padding|0x07|1|Null Padding| |Attribute Name|0x08|variable|속성 이름| ### Non-Resident Header ![[%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-05-02_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_11.00.41.png]] ![[%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-05-02_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_11.00.28.png]] |Name|Offset|Size(byte)|Description / Value| |---|---|---|---| |Starting VCN of Run List|0x10|8|Attribute의 Content를 담고 있는 첫 VCN의 값| |Ending VCN of Run List|0x18|8|Attribute의 Content를 담고 있는 마지막 VCN의 값| |Offset to Run List|0x20|2|Run List가 시작되는 오프셋| |Compression Unit Size|0x22|2|Common header에서 압축 속성 Flag가 사용중일 경우, 압축 단위| |Padding|0x24|4|Null Padding| |Allocated Size of Attribute Content|0x28|8|Attribute Content의 할당된 크기 (8 바이트 단위)| |Real Size of Attribute Content|0x30|8|Attribute Content 의 실제 크기| |Initialized Size of Attribute Content|0x38|8|Attribute Content의 초기화된 크기 <br>(일반적으로는 Real Size와 같은 값)| |Attribute Name|0x40|variable|Attribute 이름 ( 있는 경우만)| > [!Question] VCN이란? > > Virtual Cluster Number의 약자로 가상의 클러스터 번호를 의미한다. > 하나의 속성 값이 크기 때문에 다른 클러스터를 할당받아 사용하게 되는데, 이때 사용되는 클러스터의 번호가 되겠다. 실제로 볼륨에서 순차적으로 사용하는 클러스터 번호는 Logical Cluster Number (LCN)이라 한다. > VCN을 LCN과 매핑하는 방법은 Run List에서 설명하도록 하겠다. > ### **Cluster Run(Run List)** Cluster Run은 하드디스크 공간 할당 정책의 부산물이다. 여러 파일이 저장되고, 할당과 할당 해제를 반복하게 된다면 나중에 할당받게 되는 클러스터는 연속적일 수 없다. 이에 따라 비 연속적으로 저장되는 클러스터를 관리하기 위한 방법이 바로 Cluster Run이다. 이런 Cluster Run을 표현하기 위한 하나의 방법으로 Run List를 사용하게 된다. Run List는 첫번째 바이트가 바로 헤더이다. 하위 4비트는 사용하는 Run의 길이를 표현하고, 상위 4비트는 사용하는 Run의 오프셋의 크기를 지정한다. Run의 길이라는 것은 하나의 Attribute가 사용하는 클러스터의 갯수이다. Run의 오프셋은 해당 클러스터의 위치 정보를 나타낸다. 첫번째 Run List부터 VCN을 사용하게 된다면 그 클러스터의 위치를 식별할 수 없을 것이다. 이와 같은 이유에서 Runlist의 첫번째 클러스터의 주소는 LCN을 사용하게 된다. 설명이 조금 어렵다. 좀더 쉽게 이해하기 위해 실제 디스크 이미지를 가지고 실습을 해보도록 하자. 다음과 같은 Run List가 있다고 생각해 보자. ![[%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-05-02_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_11.24.07.png]] 첫번째 Run List는 7개의 클러스터를 사용하는 0xE4B 오프셋의 클러스터이며, 첫 Run List이므로 LCN이 아닌 VCN을 사용한다. 이 때 최소 단위가 하나의 클러스터이므로 실제 주소는 `0xE4B * 0x1000 = 0xE4B000`이 될 것이다. 실제 위치를 확인해 보니, 임시로 생성한 파일의 내용이 있는 것을 확인할 수 있다. ![[%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-05-02_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_11.25.33.png]] 그렇다면 Run List의 두번째 요소를 살펴보도록 하자. 두번째 Run List는 0x18개의 클러스터를 사용하며 그 주소는 0x23 오프셋의 클러스터이다. 첫번째 Run List 인자가 아니므로 이때 클러스터 주소는 VCN을 사용한다. 즉 이전 Run List 인자가 첫번째 VCN[0] 이라면 두번째 인자는 VCN[0x23]인 것이다. 즉 두번째 Run List를 따라가기 위해서는 VCN을 LCN으로 변환해야 하는데, VCN[0]의 주소를 구했으므로 VCN[0]의 주소에 오프셋을 더해주면 된다. 이전에 구한 VCN[0]의 주소는 0xE4B000이고 여기에 0x23번째 클러스터이므로 0x23 * 0x1000을 더해준다면 0xE6E000 이 다음 클러스터의 주소가 될 것이다. 실제 위치를 확인해 보면 다음과 같이 파일 내용이 연속적으로 나타나는 것을 확인해 볼 수 있다. ![[%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-05-02_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_11.30.44.png]] ## Conclusion MFT Attribute의 첫번째 글에서는 주로 MFT Attribute의 header와 이를 이해하기 위한 구조체인 Run List에 대해서 알아보았다. 파일 크기에 따라 파일시스템이 대략적으로 파일을 어떻게 처리하는지 알 수 있는 내용을 다루었다. 또 파일을 삭제함에 따라 할당/할당 해제를 반복하는 디스크 공간을 활용하는 방법중 Run List에 대해서 알아보기도 했다. Run List의 내용을 숙지한다면 MFT 파일을 통해서 실제 파일의 위치를 찾아 직접 내용물을 들여볼 수 있어 매우 중요하다고 생각되는 내용이다. 다음 게시글에서도 중요한 내용이 많이 나오니 끝까지 같이 공부해 나간다면 좋겠다.