내용

1. 예약된 영역

 

1)예약된 영역

- FAT 파일 시스템에서 가장 앞쪽에 위치하고 있는 영역으로 미래를 대비해서 비워놓은 영역이다.

- FAT 16 경우 크기가 보통 '1'이며, FAT32 경우 '32' 값을 가진다.

- 주된 사용 목적은 부트 레코드 저장과 주요 항목 백업이며 FAT16 FAT 32 모두 예약된 영역  번째 섹터에 반드시 부트 레코드를 담고 있어야 한다.(볼륨의 첫번째 섹터 이기도 하다.)

- FAT32 경우 예약된 영역 2번째 섹터에 FSInfo 구조체를 저장하며, 6번째 섹터에 부트 레코드를 백업해 놓고 있다. 만약  번째 섹터에 부트 레코드가 망가지면 백업해 놓은 부트 레코드를 이용해서 망가진 정보를 복구   있다.

 

[예약된 영역의 섹터 사용 위치  용도]

파일시스템

예약된 영역 크기

사용 용도

FAT16

보통 1 섹터

- 예약된 영역의  번째 섹터에 부트 레코드 저장

- 나머지 섹터들은 사용되지 않지만 '0'으로 초기화 시킴

FAT32

보통 32섹터

- 예약된 영역의  번째 섹터에 부트 레코드 저장

- 예약된 영역의  번째 섹터에 FSInfo 구조체를 저장함

- 예약된 영역의 6 번째 섹터에 부트레코드 사본 저장

- 나머지 섹터들은 사용되지 않지만 '0'으로 초기화

 

 

 

2)FileSystem Information 구조체 분석

 

[FSInfo 영역 출력 화면]

 

[FSInfor 항목]

 

이름

Lead Signature

위치

(Offset)

0~3

크기

(Size)

4 Byte

일반적인 

(Value)

항상 0x41615252

설명

FSInfo 섹터라는 것을 표시해주는 항목으로 항상 값이 일정하다.

0x41615252(aARR)  그림에선 Little endian 의해 거꾸로 표시  것을   있다.

 

이름

Reserved1

위치

(Offset)

4~483

크기

(Size)

479 Byte

일반적인 

(Value)

0

설명

미래를 위해 예약된 영역으로, 섹터의 대부분을 차지하며 전부 0으로 채워져 있고, 사용되지 않는다.

 

이름

Struct Signature

위치

(Offset)

484~487

크기

(Size)

4 Byte

일반적인 

(Value)

항상

0x61417272 

설명

FSInfo 섹터라는 것을 표시해주는  하나의 항목으로 항상 값이 일정하다.

0x61417272(aArr)  그림에선 Little Endian 의해 거꾸로 표시  것을   있다.

 

이름

Free Cluster Count

위치

(Offset)

488~491

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

볼륨상 존재하는  클러스터의 수를 저장.

이항 목이 0xFFFFFFFF 라면  클러스터의 수를   없는 상태 이므로 계산 되어야 한다.

정확한 값은 아니므로 참고만 하는 것이 좋다.

 

이름

Next Free Cluster

위치

(Offset)

492~495

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

해당 볼륨에서 보다 빨리  클러스터가 있는 곳을 알아내기 위해 항목으로 마지막으로 할당된 클러스터의 값이 저장된다.

 클러스터를 찾기 위해 FAT 영역을 처음부터 뒤질 필요 없이 여기에 적혀 있는 클러스터의 뒤쪽부터 찾으면 된다.

0xFFFFFFFF라면  항목에 아직 아무 것도 갱신된 사항이 없다는 의미로 어쩔  없이 처음 클러스터부터 찾기 시작해야 한다.  항목 역시 반드시 정확한 값이 아닐  있으므로 참고로만 이용하는 것이 좋다.

 

이름

Reserved 2 

위치

(Offset)

496~507

크기

(Size)

12 Byte

일반적인 

(Value)

0

설명

미래를 위해 예약된 영역으로, 섹터의 대부분을 차지하며 전부 0으로 채워져 있고, 사용되지 않는다.

 

이름

Trail Signature

위치

(Offset)

508~511

크기

(Size)

4 Byte

일반적인 

(Value)

0xAA550000

설명

FSInfo 섹터라는 것을 표시해 주는  하나의 항목으로 언제나 값은 0xAA550000으로 일정 해야 한다.  그림에선 Little Endian 의해 거꾸로  것을   있다.

 

2.FAT 영역

1)FAT 영역 분석

- FAT 파일시스템에서 매우 중요한 정보를 담고 있는 영역으로 주로 파일(or 디렉토리) 할당을 관리하는 것이 목적이다.

- FAT 영역은 특별한 구조체가 존재하는 것이 아니라 클러스터의 상태 값을 담고 있는 공간이 연속되는 단순한 형태이다.

 

[FAT32 파일시스템의 FAT1 영역]

 

- FAT32 파일시스템에서 FAT #1 영역으로 #2 #1 복사본 이므로 동일한 값을 가진다.

- 각각의 4Byte 'FAT Entry'라고 불리며 정보들이 담기게 된다.

 

[FAT32 파일시스템의 FAT 영역 항목]

 

- 0 Entry 1 Entry 제외한 나머지는 모두 클러스터를 나타내고 있다.

0번과 1 클러스터에 해당하는 Entry 없는 것을   있다. 실제로 클러스터 0번과 1번은 존재하지 않는다.

FAT 파일시스템이 FAT 영역의 0번과 1 Entry 다른 용도로 사용하면서 생긴 현상이다.

 

2) FAT 클러스터 연결 방식

- FAT 영역은 단일 링크드 리스트(Linked List) 표현하고 있으며, 각각의 FAT Entry들은 자신의 다음 클러스터(Next Cluster) 값을 담고 있다.

 

 

-  그림을 보고 한번 알아보자.

Cluster 2 위치한 값을 보게 되면 Little Endian으로 되어 있기 때문에 0x00000009 인걸   있다.  Cluster 9 나타낸다.

Cluster 9 보게 되면 0x0000000A (10진수로10)  나타내어 Cluster 10 나타내고 있다.

Cluster 10 보게 되면 0x0000000B (10진수로 11) 나타내어 Cluster 11 나타내고 있다.

Cluster 11 보게 되면 0x00000011 (10진수로 17) 나타내어 Cluster 17 나타내고 있다.

Cluster 17 보게 되면 0x0FFFFFFF 나타내며  것은 해당 파일(또는 디렉토리) 링크드 리스트 끝을 표시 하고 있는 것으로 EOC(End Of Cluster) 값이라고 한다.

 

아래 같은 색상에 Linked List 한번 파악 해보자.

- 루트 디렉토리의 시작 클러스터의 위치는 FAT 32 경우 부트 레코드의 Root Cluster 항목에 있고, FAT 16 경우 무조건 FAT 영역 바로 뒤에 오는 것은 정해져 있지만, 다른 일반 파일이나 디렉토리의 경우 시작 클러스터는 Directory Entry 통해서   있게 된다.

 

3)FAT Entry 상태 

- 위에서 알아본 것을 확인 하자면 FAT Entry 값은 자신의 다음 클러스터 번호이고 자신이 마지막 클러스터라면 0x0FFFFFFF 들어가는 것을 알게 되었지만 실제로 Entry 들어가는 상태 값을 알아 보자.

 

FAT16

FAT32

설명

0x0000

0x?0000000

비어있는 클러스터(Free Cluster), 누구에게도 사용되지 않고 연결되길 대기하고 있는 클러스터

0x0001

0x?0000001

예약된 클러스터(Reserved Cluster), 아직 특별 의미는 없으나 앞으로 사용할 값으로 예약됨

0x0002 ~ 0xEEEF

0x?00000002 ~ 0x?FFFFFEF

사용하고 있는 클러스터(Allocated Cluster), 누군가에게 연결되어 내용을 담고 있는 클러스터, FAT Entry 담긴 값은 자신의 다음 클러스터 번호이다.

0xFFF0 ~ 0xFFF6

0x?FFFFFF0 ~ 0x?FFFFFF6

예약된 클러스터(Reserved Cluster), 아직 특별한 의미는 없으나 앞으로 사용할 값으로 예약됨

0xFFF7

0x?FFFFFF7

불량 클러스터(Bad Cluster),  클러스터에 속해 있는   이상의 섹터에 불량 섹터(Bad Sector) 발생하여  이상 데이터를 저장할 능력이 없는 클러스터, 이러한 불량 클러스터는 사용하지 말아야 한다.

0xFFF8 ~ 0xFFFF

0x?FFFFFF8 ~ 0x?FFFFFFF

파일의 마지막 클러스터(EOC),  클러스터가 저장한 내용은 데이터의 끝으로  이상 뒤에 연결된 클러스터가 없다.

 

- 0 클러스터와 1 클러스터는 존재 하지 않기 때문에 비어 있거나 예약된 클러스터로 사용될  있다.

 

[FAT32 파일 시스템 FAT Entry 형태]

- FAT32 FAT Entry 경우 32bit이지만 실제 상위 4bit(Reserved) 사용되지 않고 어떤 값이 와도 무시 된다. (0x30000000 이나 0x0000000 이나 전부 비어있는 클러스터를 의미)

- Reserved 영역의 경우 어떤 값이 와도 무시되기 때문에 주로 0으로 채운다. 따라서 FAT32 표현할  있는 클러스터의 개수는 228개가 되는 것이다.

- Windows 경우 FAT Entry 상위 4비트는 건들지 않는다. 만약 0x30000000으로 비어있는 클러스터로 저장을 했다면 0x3 보존하여 불량 클러스터가 의미하는 0x?FFFFFF7 쓰면 0x3FFFFFF7이라는 갑이 저장된다. 상위 4비트의 값을 바꾸는 경우는 볼륨을 포맷하는 경우뿐이다.

 

4) FAT32 FAT16 FAT영역 차이

- FAT 16 경우 Entry 크기가 16bit(2byte)이고, FAT32 (4Byte)이다.

- 구조는 동일하나 표현할  있는 클러스터 수가 다르며 FAT32  많은 클러스터 수를 표현할  있기 때문에 대용량 하드에 적용 가능하다.

 

- FAT Entry 0번의 경우 Media Type 저장하는 용도로 사용되고 있는 것을   있었다.

 

[FAT32 파일 시스템의 FAT Entry 0번의 형태]

-  그림처럼 FAT Entry 0번은 하위 8bit Boot Record Media 값과 동일하게 적어주고 나머지는 0 채워주게 된다.

- Boot Record 저장된 Media 항목의 값이 고정식 디스크를 뜻하는 0xF8 이라면 FAT 32 0 Entry값은 0x?FFFFFF8   것이고 FAT 16 0 Entry 0xFFF8  것이다.

 

[FAT Entry 1 사용비트]

FAT 16

FAT32

Clean Shutdown Bit Mask

0x8000

0x8000000

Hard Error Bit Mask

0x4000

0x4000000

 

- Clean Shutdown Bit Mask : FAT Entry 1번의 최상위 비트로  비트 값이 0이라면 OS 볼륨을 탑재했던 마지막 순간에 볼륨을 올바르게 분리 하지 않았다는 것을 나타낸다.  비트의 값이 1이라면 정상이다.

- Hard Error Bit Mask :  비트는 FAT Entry 1번의 최상위 2번째 비트로,  비트 값이 0이면 OS 마지막 탑재  볼륨상에서 I/O 에러가 발생한 것이다. 이것의 경우 볼륨상 몇몇 섹터가 불량이 되었다는 것을 나타낸다.  비트의 값이 1이라면 정상이다.

- FAT Entry 0번과 FAT Entry 1번에 저장되는 Media Type Volume State  구현되지 않아도 파일시스템의 구현에 지장이 없다.

 

3. 데이터 영역

    

1) 데이터 영역

- 데이터들이 저장되는 영역으로 볼륨에서 대부분을 차지하고 있으며 실제  영역을 관리하기 위한 보조적인 영역이다.

 

[Data 영역 구조]

- 다른 영역과 달리 효율성을 위해 논리적인 영역 분할 단위인 클러스터로 접근 된다.

- 클러스터에는 파일이나 디렉토리의 내용을 담을  있으며, 데이터의 크기가  경우 여러 클러스터에 나누어서 담을  있다.

- 그러나  공간이 남는다고 해서 클러스터 하나에 여러 데이터의 내용을 같이 넣을 수는 없다.

 

- Data 영역에는 Directory Entry 불리는 구조체를 담고 있는 영역과 파일 이라는 2개의 형태의 데이터가 저장된다.

- Directory 담고 있는 내용이 Directory Entry 라는 것만 빼고 파일과 형태나 접근 방법이 동일하다.

- Directory Entry 저장되는 내용은 자신에게 속한 파일들과 자신의 하위 디렉토리 대한 정보들이 있다.

 

2) 원하는 파일 찾기

 

[루트 디렉토리의 Entry]    

\DOC\A.TXT 파일의 내용을 찾을 경우

첫번째, 루트디렉토리가 담고 있는 Directory Entry 하나씩 조사해서 'DOC'라는 디렉토리가 있는지 알아낸다.

두번째, Directory Entry DOC라는 디렉토리 정보와 Cluster 정보를 담고 있다.

 

[DOC 디렉토리 Entry]

세번째, DOC 디렉토리의 Directory Entry 조사하면 A.TXT 파일의 클러스터를 찾을  있다.

네번째, A.TXT 클러스터(0x4) 읽어서 화면에 출력하면 되는 것이다.

 

3) Directory Entry 항목

 

[루트 디렉토리 출력 화면]

 

- 32Byte 단위로 Directory Entry 하나가 구성되어 이다.  1섹터당 Directory Entry 16 담을  있게 된다.

 

[Directory Entry 항목]

 

이름

Name

위치

(Offset)

0~7 

크기

(Size)

8 Byte

일반적인 

(Value)

파일명

설명

파일 이름 또는 디렉토리 이름을 적는 항목으로 최대 8자리까지 적을  있으며, 대문자만 넣을  있다. 만약 이름이 남는 경우 0x20이라는 값으로 채운다. Null(0x0)으로 채우는 것이 아니다.

 

[Name[0] 값에 대한 의미]

Name[0] 

설명

0xE5

해당 디렉토리 엔트리가 담고 있는 데이터는 삭제  데이터라는 것을 의미하며, FAT 파일시스템에서는 파일이나 디렉토리가 삭제되면 해당 디렉토리 엔트리를 초기화 하는 것이 아니라 Name 항목의  번째 바이트를 0xE5 바꾼다.  파일이 삭제 되어도 복구가 가능하다.

0x00

해당 디렉토리 엔트리가 비어있고, 뒤에 오는 디렉토리 엔트리들도 전부 비어있다는 의미로 디렉토리 엔트리를 검색하다가 0x00 만나면  이상 검색하지 않아도 된다.

0x05

 바이트에 대한 실제 파일 이름 문자는 0xE5이다. 삭제된 파일데이터를 의미하는 0xE5 일본문자(간지) 집한 선두 바이트 값으로 일본어를 파일명으로 하면 삭제된 데이터로 인식을 해버리는데 0x05라는 것을 일본어 문자 선두 바이트 0xE5 표현하고 있다.

 

- Name  번째 바이트에는 0x20(스페이스)   없으며, 0x05 제외한 0x20보다 작은 값은 불가능 하다.

 

이름

Extender

위치

(Offset)

8~10 

크기

(Size)

3 Byte

일반적인 

(Value)

확장자

설명

확장자를 넣는 항목으로 최대 3자리까지 적을  있으며 반드시 대문자만 가능하다. 확장자가 2자리 이하  경우 0x20이라는 값으로 채워 넣어야 한다. 디렉토리의 경우 확장자가 없기 때문에 0x20값으로 전부 채우게 된다.

 

이름

Attribute 

위치

(Offset)

11 

크기

(Size)

1 Byte

일반적인 

(Value)

속성 항목 확인

설명

Directory Entry 어떤 용도 인지 결정하는 항목으로 해당 Directory Entry 담고 있는 것이 파일인지 디렉토리인지는 바로  항목에 저장된 값을 통해 결정된다.

 

[속성 항목 ]

속성 

속성 이름

설명

0x01

Read Only

읽기 전용 파일.  속성이 걸려 있는 파일은 쓰기를 막도록 코드를 짜야 한다.

0x02

Hidden

보통의 사용자로부터 파일을 보여 주지 않는다.

0x04

System

OS에서 사용하는 파일

0x08

Volume Label

 파일의 이름이 볼륨레이블이 된다.  속성은 반드시 루트에 있어야 하며, 1개만 존재해야 하고,  속성을 가진 파일은 FstClusHi FstClusLow 값이 '0' 이어야 한다.(볼륨 레이블에는 클러스터를 할당할 필요가 없다.

0x10

Directory

서브 디렉토리를 의미한다.

0x20

Archive

일반적인 파일을 의미한다.

0xF0

Long File

Name Entry 

해당 Entry Directory Entry 아니라 Long File Entry 이다.

 

 

 

이름

NT Resource

위치

(Offset)

12

크기

(Size)

1 Byte

일반적인 

(Value)

0x0

설명

NT 계열의 Windows OS 사용하기 위해 예약한 공간으로 0으로  설정을 한다.

 

이름

Create Time Tenth

위치

(Offset)

13

크기

(Size)

1 Byte

일반적인 

(Value)

0~199

설명

파일이 생성된 시각을 1/10 단위로 기록한 항목. Create Time 항목과 관련이 있다.

 

이름

Create Time 

위치

(Offset)

14~15 

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

파일 생성 시간. 표현 형식에 대해서 아래 항목 참고.

 

[Create Time 항목]

 

[Create Time 항목 내용]

속성 

유효 범위

설명

Seconds

0 ~ 29

초를 기록하며 값이 1 증가하면 초는 2 증가 한다.

 값이 4 8, 값이 22 44초를 나타낸다.

Minutes

0 ~ 59

분을 기록

Hours

0 ~ 23

시를 기록

 

- 00:00:00 부터 23:59:58 까지 유효한 값의 범위로 사용 가능.

 

이름

Create Date

위치

(Offset)

16 ~ 17 

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

파일 생성 날짜. 표현 형식

 

[Create Date 항목 구조]

 

속성 

유효 범위

설명

Day 

1 ~ 31 

 기록

Month 

1 ~ 12 

 기록

Year 

0 ~ 127 

년도 기록. 기준 년도 1980년에 저장된 값에다 더하여 출력함.

값이 30이라면 1980 + 30 = 2010년이라는 의미로

1980~2107년까지 표현 가능

 

이름

Last Access Date 

위치

(Offset)

18 ~ 19 

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

 항목은  파일에 읽기/쓰기 작업을 했던 마지막 날짜를 기록한다. 최근 접근 날짜만 있고 최근 접근 시간에 대한 항목은 없다. 마지막 작업이 쓰기였다면  항목의 날짜와 Write Date 날짜가 일치해야 하며, Create Date 형식과 동일하다.

 

이름

First Cluster High 2Bytes

위치

(Offset)

20 ~ 21

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

 항목은  파일의  번째 클러스터 번호의 상위 2Byte 담고 있으며, FAT16에서는 클러스터 번호가 2Byte 이루어 져있기 때문에  항목이 필요가 없다. FAT 16에서는 항상 0이다.

 

이름

Write Time

위치

(Offset)

22 ~ 23

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

가장 최근에  파일을 수정한 시간. 파일 최초 생성도 쓰기로 간주하며  항목 역시 Create Time 형식과 동일하다.

 

이름

Write Date

위치

(Offset)

24 ~ 25

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

가장 최근에  파일을 수정한 날짜. 파일 최초 시간도 쓰기로 간주하며  항목의 형식 역시 Create Date 형식과 동일하다.

 

 

이름

First Cluster Low 2Byte 

위치

(Offset)

26 ~ 27 

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

 항목은  번째 클러스터 번호의 하위 2Byte 담고 있으며, FAT 16에서는 클러스터 번호의 크기가 2Byte이므로 굳이 First Cluster High 조사하지 않아도  항목만으로도 충분하다.

 

이름

File Size 

위치

(Offset)

28 ~ 31 

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

 항목은 해당 파일의 크기를 나타내며, 표현 단위는 Byte 표현할  있는 최대 크기는 4GB이다. 파일 하나가 4GB 넘을  없다.

 

[FTK Imager] 이용한 파일 덤프하여 간단한 분석을 해보았다.

디렉토리의 경우 File Size 0 것을   있고. 삭제된 파일, 일반 파일의 경우 Name[0]에서 차이를 보이는 것을  수있다.

 

4) 파일명 문자 제한

- FAT 파일시스템에서 파일명(확장자 포함) 또는 디렉토리명으로 허용되는 문자에는 제한이 있는데 긴파일명(LFNs) 짧은 파일명의 허용 제한 범위가 다르다,

- Short File Name(짧은 파일명) 문자 제한 범위를 알아보자

영어 대문자 A~Z(반드시 대문자여야 한다.)

해당 OS 지원하는 언어의 문자여야 한다.

아라비아 숫자 0~9

특수 문자 $%'=_@~!(){}^#& 가능

 

[Short File Name]

파일명

Name + Extender(11byte)

FOO.BAR

F

O

O

         

B

A

R

FILEDATA.DOC

F

I

L

E

D

A

T

A

D

O

C

foo

F

O

O

               

foo.bar

F

O

O

         

B

A

R

Pickle.A

P

I

C

K

L

E

   

A

   

.BIG

잘못된 표현으로 Name[0] 0x20   없다.

HELLO!.JPG

잘못된 표현으로 '!' 허용  된다.

 

 

4. Long File Names

1) Long File Names

LFNs 주요 특징을 알아보자.

유니코드(UTF-16)방식으로 인코딩되어 다국어 지원이 가능 하다.

해당 OS 지원하는 언어의 문자여야 한다.

아라비아 숫자 0~9

특수 문자 $%'=_@~!(){}^#& 가능

- LFNS 설계하면서 호환성은 얻었지만 Long File Name Entry 구조가 어색하고 불편하게 되어 버렸다.

- 유니코드를 지원하면서 여러 나라에서 사용될  있었다.

 

Long File Name Entry 하나에는 최대 13자의 문자열을 저장할  있다. 따라서 파일명을 255자로 한다면 LFNs Entry 무려 20개나 차지하게 되고, FAT16에서는 루트 디렉토리의 Entry 보통 512 제한되기 때문에 FAT 16에서 LFNs 사용하게 되면 루트 디렉토리에 많은 수의 파일을 저장할  없게 된다.

파일을 하위 디렉토리에 저장하거나, 볼륨을 포맷할  Entry 개수를 많이 잡게 되면 해결이   있다.

 

 

2) Long File Name Entry 항목 분석

 

[Long File Name Entry 담긴 섹터 화면]

"FileSystem Forensic.txt"이란 파일을 저장한 화면 이다.

 

 

이름

Order

위치

(Offset)

0 

크기

(Size)

1 Byte

일반적인 

(Value)

가변적

설명

Long File Name Entry 정렬된 순번을 기록 하는 항목.  항목의 6번째 비트(0x40) 1이라면 해당 파일명을 구성하는 마지막 Entry 의미한다.

 

이름

Name1

위치

(Offset)

1~10 

크기

(Size)

10 Byte

일반적인 

(Value)

가변적

설명

해당 Entry 저장할 문자열  1~5번째 문자열을 여기에 저장하게 된다.

 

이름

Attribute

위치

(Offset)

11 

크기

(Size)

1 Byte

일반적인 

(Value)

0x0F

설명

가장 최근에  파일을 수정한 날짜. 파일 최초 시간도 쓰기로 간주하며  항목의 형식 역시 Create Time 형식과 동일하다.

 

이름

Type 

위치

(Offset)

12 

크기

(Size)

1 Byte

일반적인 

(Value)

일반적으로 '0'

설명

 항목의 값이 '0'이라면 Long File Name 항목  하나라는 의미로, 다른 값들은 미래를 위해 예약 되어 있다.

 

이름

Check Sum 

위치

(Offset)

13 

크기

(Size)

1 Byte

일반적인 

(Value)

일반적으로 '0'

설명

 항목은 Short Directory Entry 항목에 들어가는 11자의 문자열에 대한 Checksum 저장한다.

 

- Check Sum Long File Name Entry Checksum이라고 생각하기 쉽지만 Windows  항목에 Short Directory Entry 11자의 문자열에 대한 Checksum 구해서 저장한다.

[저장 함수]

unsigned char ChkSum (char *buf){

    int i;

    unsigned char Sum=0;

 

    for(i=0;i<11;i++)     { //우측으로 회전하면서 더하기

        Sum = ((Sum & 1) ? 0x80 : 0) + (Sum >> 1) + buf[i];

    }

    return sum;

}

 

이름

Name2

위치

(Offset)

14~25 

크기

(Size)

12 Byte

일반적인 

(Value)

가변적

설명

해당 Entry 저장할 문자열  6~11번째 문자열을 저장함

 

이름

First Cluster Low 

위치

(Offset)

26~27 

크기

(Size)

2 Byte

일반적인 

(Value)

반드시 '0'

설명

 항목은 Long Directory Entry에서는 의미 없는 값이지만, 기존의 FAT 코드와의 호환성을 위해 반드시 0으로 해야한다.

 

이름

Name3 

위치

(Offset)

28~31 

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

해당 Entry 저장할 문자열  12~13번째 문자열을 저장함

 

 

3) LFNs 파일명 인코딩(Encoding)

- 위에서 보면 Long Directory Entry  개당 13개의 문자열을 저장할  있는데 저장형태가 2바이트당 1개의 문자열을   있다. 이는 LFNs 유니코드(UTF-16)으로 하기 때문이다.

 

 

4) Short Directory Entry Long Directory Entry 관계

- Long Directory Entry  자체만으로는 파일명만 저장하는 기능뿐이다. 파일 관리하는데 있어서 독립적으로 이용될  없고, 반드시 대응하는 Short Directory Entry 있어야 한다.

- Short Directory Entry Long Directory Entry 저장되는 형태는 다음과 같다.

 

[Directory Entry 순서]

Directory Entry 순서

Order 항목 

N번째 Long File Name Entry(마지막 Entry)

LAST_LONG_ENTRY(0x40) | N

 

2 번째 Long File Name Entry

0x02

1 번째 Long File Name Entry

0x01

위의 Long File Name Entry 대응 하는 Short Directory Name Entry

(해당 사항 없음)

 

- 마지막에 Short Directory Entry 위치하고, 위로 Long Directory Entry 시작 되어  위쪽에 마지막 문자열의 Long File Name Entry 위치한다.

- 마지막 Long File Name Entry에는 마지막이라는 것을 알려주기 위해Order 항목에 해당 Entry 번호와 0x40 비트를 OR한다. Windows LNF 파일명을 최대 255 제한하고 있다.

- 1개의 Long File Name Entry 13개의 문자열을 저장할  있기 때문에 최대 20개의 Long File Name Entry 필요하다는 것을   있으며 20번째 Entry Hex 값으로 0x14이기 때문에 0x40까진   없다 따라서 40번째 Long File Name Entry 생기지 않을 것이다. 255  채울 경우 하더라도 '0x40 | 0x14 = 0x54' 되는 것이다.

 

5) Long File Name Entry

- LFNs에서는 파일명과 확장자 사이에 들어가는 (.)까지도 문자열로 처리하고 있다. 그리고 LFNs에서 마지막 문자열까지 저장 완료 했다면 다음에 오는 공간에 0x0000 저장하여 문자열이 끝났음을 알려주며,  이후 남는 공간은 전부 0xFFFF 처리하면 된다.

-  문자열이 Long File Name Entry 정확히 맞아 떨어지면 0x0000 넣기 위해 Long File Name Entry 하나  사용하지 않고 그냥 0x0000 넣지 않는다.

 

[ "The quick brown.fox" Long File Name Entry예제 ]

 

- LFNs에서 마지막 문자열까지 저장이 완료되면 다음에 오는 공간에 0x0000 저장하여 문자열이 끝났음을 알리며  후에 남는 공간은 0xFFFF 처리된다.

- 만약 Long File Name Entry 정확히 맞아 떨어지면 0x0000 넣기 위해 Long File Name Entry 추가하여 사용하지 않는다.

 

 

- "FAT32 File system docs.txt" 라는 26개로 Long File Name Entry  개당 13개씩 문자열 정확히 2개의 Entry 떨어지는 문자열 뒤에 0x0000 없게 된다.

 

 

6) LFNs 파일명 문자 허용 범위

 

[Long File Names에서 허용되는 문자 범위]

유니코드(UTF-16)방식으로 인코딩되어 다국어 지원이 가능 하다.

아라비아 숫자 0~9

Short File Name에서 허용되는 특수 문자

(특수 문자 $%'=_@~!(){}^#& 가능)

Long File Names에서 추가적으로  허용되는 특수 문자

(특수 문자 + , ; = [ ])

 

허용되지 않는 문자 : \ / : * ? " < > |

 

 

 

7) Short File Name 변환 규칙

- Long File Name 반드시 자신과 대응하는 Directory Entry 가져야 한다.

- Directory Entry에는 Long File Name 별칭(Alias) 가진 Short File Name 있어야 하며, Short File Name 생성하는 규칙은 엄격하지 않기 때문에 마음대로 지정하여도 상관 없다.  허용하지 않는 문자 범위를 넘어서면  된다.

 

[Windows Long File Name 별칭을 생성하는 과정]

    1_영어 소문자를 전부 대문자로 변환

    2_Short File Name 문자로 허용되지 않는 문자들은 전부 '_' 변환한다.

    3_문자열 사이에 모든 Space(0x20) 제거 한다.

    4_문자열 사이에 있는 모든 ' .' 제거 한다.

    5_위의 공식대로 얻어진 문자열에서 앞의 6자리만 얻어낸다.

    6_마지막 2 문자열은 ~ 넣은  숫자를 적는다. 숫자는 1부터 시작한다.    

    7_만약 이렇게 생성한 파일명이 존재한다면 숫자를 증가시킨다.

    8_확장자는 앞에서 3자리만 넣어준다.

 

[Long File Name 별칭 생성]

파일명

Name + Extender(11byte)

test doc1.txt

T 

E 

S 

T

D

O

~

1

T 

X 

T

test docs.txt 

T 

E 

S 

T 

D

O 

~ 

1 

T

X 

T 

man.songs 

M 

A 

N 

~

1

     

S

O

N

my.name.txt 

M 

Y

N

A

M

E

~

1

T 

X 

T 

File System.jpg 

F 

I 

L 

E 

S 

T 

~

1

J 

P 

G 

 

 

 

5. Data 영역 실습

 

먼저 FAT 16으로 포맷을   안에 파일 여러 개를 넣어 놓도록 하자.

 

Visual Studio 2012 관리자 권한으로 실행한다.

 

관리자권한으로 실행

프로젝트 속성  구성 속성  일반 -> 프로젝트 기본값  문자집합 : 멀티 바이트 문자 집합 사용

 

#include <stdio.h>

#include <Windows.h>

#include <stdlib.h>

#pragma pack(1)

 

#define U8        unsigned char

#define S8        char

#define U16        unsigned short

#define U32        unsigned int

#define U64        unsigned __int64

 

typedef struct _DIR_struct{ //Directory Entry 구조체

    U8            Name[11];

    U8            Attr;

    U8            NTres;

    U8            CrtTimeTenth;

    U16        CrtTime;

    U16        CrtDate;

    U16        LstAccDate;

    U16        FstClusHi;

    U16        WriteTime;

    U16        WriteDate;

    U16        FstClusLow;

    U32        FileSize;

}DirEntry;

 

typedef struct _FAT16_BPB_struct{ //FAT16 부트 레코드 선언

    //공통 영역

    U8            JmpBoot[3];

    U8            OEMName[8];

    U16        BytsPerSec;

    U8            SecPerClus;

    U16        RsvdSecCnt;

    U8            NumFATs;

    U16        RootEntCnt;

    U16        TotSec16;

    U8            Media;

    U16        FATs16;

    U16        SecPerTrk;

    U16        NumHeads;

    U32        HiddSec;

    U32        TotSec32;

    //FAT 16영역

    U8            DirveNumber;

    U8            Reserved1;

    U8            BootSignal;

    U32        VolumeID;

    U8            VolumeLabel[11];

    U8            FileSysType[8];

 

    U8            BootCodeArea[448];

 

    U16        Signature;

}FAT16_BPB;

 

typedef struct _LONG_DIR_struct{ //Long File Name Entry 구조체

    U8            order;

    U8            Name1[10];

    U8            Attr;

    U8            Type;

    U8            chksum;

    U8            Name2[12];

    U16        FstClusLo;

    U8            Name3[4];

}LongDirEntry;

#pragma pack()

 

typedef struct _VOL_struct{ //볼륨의 전반적인 정보를 담는 구조체

    U32        Drive;

    U32        VolBeginSec;

    U32        FirstDataSec;

    U32        RootDirSec;

    U32        RootEntCnt;

    U32        RootDirSecCnt;

    U32        FATSize;

    U32        FATStartSec;

    U32        TotalClusCnt;

    U32        TotalSec;

    U32        DataSecSize;

    U32        ClusterSize;

    U32        SecPerClus;

}VolStruct;

 

void print_longName(LongDirEntry* pLongDir, U32 EntryNum);

U32 show_dir(DirEntry* pDir);

U32 get_BPB_info(FAT16_BPB* BPB, VolStruct* pVol);

U32 HDD_read(U8 drv, U32 SecAddr, U32 blocks, U8* buf);

 

VolStruct gVol;

 

int main(void){

    U8            buf[512];

    U8*        pRootBuf;

 

    gVol.Drive = 0x2; //드라이브 설정

    gVol.VolBeginSec = 0x87; //시작 섹터 설정

 

    if(HDD_read(gVol.Drive, gVol.VolBeginSec, 1, buf) == 0){

        printf("Boot Sector Read Failed \n");

        return 1;

    }

    //buf를 읽어서 처리한 뒤 gVol 변수에 담는다. FAT16전용이기 때문에 다르면 에러 발생

    if(get_BPB_info((FAT16_BPB*)buf, &gVol) == 0){

        printf("It is not FAT16 File System\n");

        return 1 ;

    }

 

    //루트 디렉토리를 읽고 메모리에 담고 있을 버퍼를 잡는다. byte 단위(섹터 512)

    pRootBuf = (U8*)malloc(gVol.RootDirSecCnt * 512);

 

    //저장장치로부터 부트 디렉토리의 섹터를 읽어 옮김

    if(HDD_read(gVol.Drive, gVol.RootDirSec, gVol.RootDirSecCnt, pRootBuf) == 0){

        printf("Root Dir Read Failed \n");

        return 1;

    }

 

    //읽어 들인 루트 디렉토리의 데이터 화면 출력

    show_dir((DirEntry*)pRootBuf);

    return 0;

}

 

U32 show_dir(DirEntry* pDir){

    U32 i, y, LongEntryEn=0;

 

    for(i=0; i<=gVol.RootEntCnt ; i++)

    {

        //Name[0]값 조사

        switch((U8) pDir[i].Name[0]){

        case 0x00 : return 1;

        case 0xE5 : continue;

        }

 

        //해당 Entry Long File Name Entry이므로 나중에 처리하도록 LongEntryEn 변수를 1로 변경

        if(pDir[i].Attr == 0xF0){

            LongEntryEn = 1;

            continue;

        }

        printf("=======Entry Number %d =======\n", i);

        //해당 Entry 출력

        if(pDir[i].Attr == 0x10)    printf("Directory Name : ");

        else                            printf("File Name : ");

 

        //print_longName함수를 호출하여 다시 조사함. 해당 Entry의 위쪽부터 저장되기 때문에 i-1

        if(LongEntryEn == 1){

            print_longName((LongDirEntry*)pDir, i-1);

            LongEntryEn = 0;

        }else{

            for(y = 0; y< 11 ; y++)

            printf("%c",pDir[i].Name[y]);

        }

        printf("\n");

        //해당 클러스터 크기, 시작 클러스터를 화면에 출력

        printf("File size        %d\n",pDir[i].FileSize);

        printf("Start Cluster : %d\n",(pDir[i].FstClusLow | pDir[i].FstClusHi << 16));

    }

    return 1;

}

 

void print_longName(LongDirEntry* pLongDir, U32 EntryNum){

    wchar_t        filename[512];

    char            final[512];

    U32            nameOffset = 0;

 

    do{     // Long File Name Entry 항목의 Order이 0x40의 비트 1이 나올 때 까지 파일명을 얻음.

        memcpy(&filename[nameOffset], pLongDir[EntryNum].Name1, 10); nameOffset +=5;

        memcpy(&filename[nameOffset], pLongDir[EntryNum].Name2, 12); nameOffset +=6;

        memcpy(&filename[nameOffset], pLongDir[EntryNum].Name3, 4);     nameOffset +=2;

    }while (( pLongDir[EntryNum--].order & 0x40) == 0);

 

    filename[nameOffset] = 0x0000;     //문자열의 끝을 알려주는 값 넣음

 

    wcstombs(final, filename, 512);     //유니코드를 ASCII 코드로 변환해준 후 화면에 출력

    printf("%s", final);

 

}

 

U32 HDD_read (U8 drv, U32 SecAddr, U32 blocks, U8* buf){

    U32 ret;

    U32 ldistanceLow, ldistanceHigh, dwpointer, bytestoread, numread;

 

    char cur_drv[100];

    HANDLE g_hDevice;

 

    sprintf_s(cur_drv,sizeof(cur_drv),"\\\\.\\PhysicalDrive%d",(U32)drv);

    g_hDevice=CreateFile(cur_drv, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    

    if(g_hDevice==INVALID_HANDLE_VALUE)    return 0;

 

    ldistanceLow = SecAddr << 9;

    ldistanceHigh = SecAddr >> (32 - 9);

    dwpointer = SetFilePointer(g_hDevice, ldistanceLow, (long *)&ldistanceHigh, FILE_BEGIN);

    

    if(dwpointer != 0xFFFFFFFF)    {

        bytestoread = blocks * 512;

        ret = ReadFile(g_hDevice, buf, bytestoread, (unsigned long*)&numread, NULL);

        if(ret)    ret = 1;        

        else        ret = 0;        

    }

    

    CloseHandle(g_hDevice);

    return ret;

}

 

U32 get_BPB_info(FAT16_BPB* BPB, VolStruct* pVol){

    //Root Entry Count 항목 조사, 0이면 FAT16이므로 파일시스템 종류 알기 좋다.

    if(BPB->RootEntCnt == 0 || BPB->Signature != 0xAA55)

        return 0;

    //부트 레코드 정보 얻기.

    if(BPB->TotSec16 !=0)    pVol->TotalSec = BPB->TotSec16;

    else                            pVol->TotalSec = BPB->TotSec32;

 

    //get FAT Size

    pVol->FATSize = BPB->FATs16;

 

    //get FAT Start Secotr

    pVol->FATStartSec = pVol->VolBeginSec + BPB->RsvdSecCnt;

 

    //get Root Dir Entry Count

    pVol->RootEntCnt = BPB->RootEntCnt;

    

    //get Root Dir Sector

    pVol->RootDirSec = pVol->VolBeginSec + BPB->RsvdSecCnt + (BPB->NumFATs * BPB->FATs16);

 

    //get Root Dir Sector Count

    pVol->RootDirSecCnt = ((BPB->RootEntCnt * 32) + (BPB->BytsPerSec -1 )) /BPB->BytsPerSec;

 

    //get FAT Start Sector

    pVol->FirstDataSec = pVol->VolBeginSec +BPB->RsvdSecCnt + (BPB->NumFATs * pVol->FATSize) + pVol->RootDirSecCnt;

 

    //get Size of Data Area

    pVol->DataSecSize = pVol->TotalSec - (BPB->RsvdSecCnt + (BPB->NumFATs * pVol->FATSize) + pVol->RootDirSecCnt);

 

    //get Total Cluster Count

    pVol->TotalClusCnt = pVol->DataSecSize / BPB->SecPerClus;

 

    //get Sector per Cluster

    pVol->ClusterSize = BPB->SecPerClus * BPB->BytsPerSec;

 

    //get Sector Per Cluster

    pVol->SecPerClus = BPB->SecPerClus;

 

    return 1;

}

[드라이브 설정]

위에 진하게 주석  부분에서 드라이브 설정을  

gVol.Drive = 0x2  것은 아래 D드라이브를 테스트 하려고  것이다.

 

만약 디스크0번을 분석하려면

gVol.Drive = 0x0 으로 설정 해야한다.

 

 

[부트레코드 섹터위치]

 

gVol.VolBeginSec = 0x87;

아래 스샷은 FTK Imager 확인을 해보았다.

 

[실행화면]

 

 

+ Recent posts