내용

 

1. Ext2

1) 리눅스와 Ext2파일 시스템의 발전

- 리눅스가 처음 만들어   사용된 파일시스템인 Minix 파일시스템을 먼저 살펴 보면, 앤드류 타넨비움 교수가 교육용 유닉스 클론으로 만든 오픈 소스 운영체제인데 이때 사용된 파일시스템이 Minix 파일시스템이라 한다.

- Minix 경우 리누즈 토발즈(리눅스의 창시자) Minix에서 리눅스를 교차 개발 하고 있었으며, 새로운 파일시스템을 고안하는거 보다 기존의 Minix 파일시스템을 지원하는 것이 파일 공유에 효율적이고, 안정적이였기 때문에 리눅스에 Minix 파일시스템을 지원하게  것이다.

- Minix 파일 시스템의 경우 제약사항이 있는데 16비트 정수로 블록 주소를 지정하여 최대 64MB까지 저장할  있었고(1024(기본 블록 사이즈) X 65536(16비트) = 64MB), 따라서 최대 파일 크기도 64MB 제한되었고, 파일명의 길이도 최대 14자였으며, 가변 블록이나 타임 스탬프도 지원하지 않았다.

 

- Minix 제약 사항으로 인해 새로운 파일 시스템이 개발되고 위의 문제를 해결한 Ext(Extended FileSystem, 확장 파일 시스템) 구현되어 리눅스 0.96c 버전에 탑재된다.

- Ext 경우 최대 2GB까지 저장가능 하고, 파일길이도 255Byte까지 사용할  있었지만 Inode 수정이 불가능하고, 데이터 수정 타임 스탬프 등의 지원이 없고, 파일시스템의 비어있는 블록이나 Inode 다루는 자료구조로 연결 리스트를 사용하여 성능이 낮은 문제 점등이 있었다. 이렇게 되어 파일시스템을 사용하면서 리스트의 특성상 정렬이 불가능하고,  노드들이 흩어지게 되버리는 문제가 발생한 것이다.

 

- Ext 여러 문제 해결을 위해 Ext2 XIA라는 파일 시스템의 알파버전이 발표되었다.

- XIA 파일 시스템은 Minix 파일시스템의 커널 코드에 크게 의존하고,  가지 사항을 개선하게 되었다.  파일명, 2GB 파일 시스템 사용 가능, 3가지 타임스탬프를 지원 하였다.

- Ext2 경우 Ext 파일시스템에 근거했지만 상당부분 재구성하고, 여러 기능을 추가하게 되었다. 또한 앞으로 있을 기능 개선을 염두에 두었던 디자인들이 추후에 많은 버그 개선과 성능향상에 도움이 되었다.

- Ext2 XIA 파일시스템이 발표되었을  기본 기능은 동일하며, 보다 단순한 디자인으로 인해 XIA  Ext2 보다 안정적으로 동작하였다.

- 파일 시스템이 점점 널리 사용되고 Ext2 파일시스템의 많은 버그들이 수정되고, 여러 기능이 추가되면서 안정적인 파일시스템으로 알려지게 되고, Ext2 저널링이 추가된 Ext3 파일시스템이 나오기 전까지 리눅스의 기본 파일시스템으로 쓰이게 되었다.

 

구분

Minix

Ext

Ext2

XIA

파일 시스템의 최대 크기

64MB

2GB

4TB

2GB

최대 파일 크기

64MB

2GB

2GB

64MB

파일명 최대 길이

16/30

255

255

248

3 타임스탬프 지원

X

X

O

O

확장성

X

X

O

X

가변 Block 크기

X

X

O

X

향후 지원

O

X

O

?

 

 

2) UFS Ext

- Ext 파일시스템은 Unix 파일시스템인 UFS(Unix File System)에서 기본 개념을 가져왔으며, UFS 마찬가지로 빠르고 안정적인 면에서 높은 파일 시스템으로 전체적으로 중요한 데이터 구조들이나 저장공간  저장장치의 활용적인 측면도 UFS에서 대부분 빌려왔기 때문에 기반이 되는 디자인측면에서 이미 검증된 파일 시스템이라   있다.

- UFS에서 많은 부분 가져오긴 했지만, Ext 단순히 UFs 클론이 아니라 UFS에서 사용되지 않는 부분이나 구조적으로 명료하지 않은 부분은 모두 제외하였기 때문에 Ext 파일시스템이 보다 명료하고 심플하게 한데 많은 도임이 되었다.

 

 

2. Ext2 구조

 

 

1) Ext2 파일 시스템 구조

[Ext2 파일시스템의 레이아웃]

 

- Ext2 파일시스템은 크게 부트 섹터(Boot Sector) 블록 그룹(Block Group)으로 이루어진다.

- 블록의 경우 Ext2 파일시스템에서 기본적으로 데이터를 저장하는 단위이며 한번에 I/O과정에서 읽어 들이는 단위가 되기도 한다. 하드디스크의 물리적인 최소 단위인 LBA 블록과는 개념이 틀리며 Ext2 파일시스템 생성시 크기 설정이 가능하다.

(리눅스에서 mke2fs 프로그램을 이용하면 파일시스템을 생성하는 경우 블록의 크기를 지정할  있는데, 범위가 1KB~4KB까지 가능하다.)

 

1) 블록 그룹(Block Group)

[블록 그룹 레이아웃]

 

- 블록들이 모인 것이 블록 그룹이며, Ext2 파일 시스템에서는 블록들을 여러 개의 그룹으로 나누어 파일시스템의 정보  데이터를 저장한다. 이때 OS 커널에서는 가능하면 파일에 속하는 데이터 블록은 같은 블록에 저장하려 하기 때문에 블록 그룹은 파일 단편화를 줄일  있다.

 

[블록들의 정보]

순서

블록 

Block 

1

Super Block

1Block

2

Group Descriptor Table

n Block

3

Block Bitmap

1 Block

4

Inode Bitmap

1 Block

5

Inode Table

n Block

6

File data Blocks

n Block

 

- Ext2 파일시스템의 전체적인 정보를 저장하는 데이터 구조가 2개가 있는데 각각 슈퍼 블록(Super Block) 그룹 디스크립터 테이블(Group Descriptor Table) 나눠 진다.

- 슈퍼블록의 경우 가장 앞부분에 존재하며 파일 시스템의 크기와 환경 설정 값들을 저장한다. FAT NTFS 부트 레코드와 비슷할 것이다.

- 그룹 디스크립터 테이블은 슈퍼 블록 바로 다음 블록에 자리하며, 주요 데이터가 손상될 경우를 대비해 슈퍼 블록과 그룹 디스크립터 테이블의 사본이 모든 블록 그룹에 걸쳐 곳곳에 저장된다. 만약 슈퍼 블록에 문제가 있다는 것을 감지하면 다른 블록 그룹에 있는 슈퍼 블록의 복사본을 이용할  있다.

- 이런 블록 구조는 주요 데이터와 파일 데이터간의 집약도를 높여 파일을 저장할  단편화를 줄이며, 디스크 I/O 관련된 응답 속도를 줄여주는 계기가 된다.

사실 정상 동작하는 경우 커널은 0 블록 그룹의 정보만을 사용하고 나머지 블록 그룹에 속한 슈퍼 블록과 그룹 디스크립터 테이블은 업데이트 되지 않는다. 실질적으로 나머지 블록 그룹들의 정보가 이용되거나 업데이트될 때는 /sbin/e2fsck 등의 프로그램이 파일시스템의 일관성 검사 수행  블록 그룹 0 저장한 슈퍼 블록과 그룹 디스크립터 테이블을 나머지 블록 그룹에 복사한다.

 

- 블록 그룹의 개수와 크기의 경우 해당 파티션과 블록 크기에 따라 달라지는데 제약 사항이 있을 경우 그룹 내의  블록들이 할당되었는지 비었는지를   있는 블록 비트맵(Block Bitmap) 1Block 내에 저장되어야 하므로 Block Bitmap 표시할  있는 최대 블록 개수는 Ext2 파일시스템을 생성할  설정한 블록 크기를 가지고 계산할  있다.

1Byte ( bit들이 데이터 블록에 해당) X 블록 크기를 4KB 가정한다며  블록의 크기 =Block Bitmap 표시할  있는 최대 블록 개수

8bit X 4096 = 32,768bit (32KB) 이다.

 

- 1Block 4KB 경우 하나의 블록 그룹이 가질  있는 최대 블록 개수

Block Bitmap 표시할  있는 블록개수 X 1Block 최대 블록 개수 = 1블록 그룹이 가질  있는 블록 개수

32KB X 4KB = 128MB

 

- 만약 10GB 파티션에 Ext2 파일시스템을 생성하면 블록 그룹의 개수는

전체 용량 / 1블록 그룹이 가질수 있는 블록 개수 = 최대 블록 그룹 개수  것이다.

1024MB / 128MB = 80

 

 

2) 슈퍼블록(Super Block)

- 슈퍼 블록은 Ext2 파일시스템에서 사용되는 주요 설정 정보들이 기록되는 영역으로 블록 그룹의  번째 블록에 위치하며, 파일시스템에서 설정한 블록의 크기가 1KB, 4KB 실제 위치하는 곳은 같다. , 디폴트 블록 크기인 1KB 경우에 맞춰 4KB 블록일 때도 3KB 패딩되지 않는다.

- 이렇게 되는 이유는 슈퍼 블록 내에 블록 크기가 정해져 있으므로 블록의 크기에 따라서 슈퍼 블록이 기록되는 물리적 위치가 변경된다면 여러 가지로 신경 써야  부분이 늘어나게  것이다.

- 슈퍼 블록은 반드시 블록 그룹의 시작부터 1024Byte 내에 기록되어야 하며, 1024Byte 크기로 저장되어야 한다.

- 사실상 1024Byte  실제 사용되는 영역 보다 사용되지 않은 영역이  많다.

- 슈퍼 블록에는 파일 시스템의 설정 파일들이 기록되어 있고, 부트 코드가 기록되어 있지는 않으며 슈퍼 블록의 사본은 모든 블록 그룹들의  번째 블록에 저장된다.

 

- 슈퍼블록에 저장되는 주요 데이터

1_ 블록의 크기(1KB, 2KB, 4KB)

2_  블록의 개수

3_ 블록 그룹의 개수

4_ Inode 개수

5_ 그룹 내의 블록/Inode 개수

 

3) 그룹 디스크립터 테이블(Group Descriptor Table)

- 슈퍼 블록의 다음 블록부터 Group Descriptor Table  위치하며,  Table 해당 파일시스템 내의 모든 블록 그룹에 대한 정보를 기록한다.

- 각각의 정보를 Group Descriptor이라고 하며, Group Descriptor Table 슈퍼 블록과 마찬가지로 모든 블록 그룹에 동일하게 중복 기록 되어 있다.

- Group Descriptor Table 저장되는 주요 데이터

1_ Block Bitmap 블록 번호

2_ Inode Bitmap 블록 번호

3_  번째 Inode Table Block 블록 번호

4_ 그룹 안에 있는  블록 

5_ 그룹 안에 있는 Inode 

6_ 그룹 안에 있는  디렉토리 

 

- 블록 그룹의 개수와 크기를 계산 하는 부분을 보고, 그룹 디스크립터 테이블 정보를 알게 되면 그룹의 크기와 그룹 내에 남은 용량  블록 그룹의 전체적인 레이아웃을 그릴  있을 것이다.

- Group Descriptor  크기를 알아보면 32Byte 하나의 Group Descriptor 기록 된다. 기본 크기(1KB)라고 한다면 하나의 블록에  32(32Byte X 32 = 1KB)  Group Descriptor 기록될  있고, 블록 그룹의 개수가 늘어나는 만큼 테이블의 크기도 커지게 되는 것이다.

 

4) 블록 비트맵(Block Bitmap)

- Block Bitmap Group Descriptor Table 다음에 위치하며, 테이블의 크기가 일정치 않기 때문에 Block Bitmap 위치도 상황에 따라 달라진다. 따라서 Group Descriptor 정보에 Block Bitmap 위치가 포함되는 것이다.

 

- Block Bitmap  그대로 블록의 사용 현황을 Bit 표현하여 나타낸 것으로 그룹 내에 존재하는 각각의 블록은 Block Bitmap에서 하나하나의 Bit 해당하게 된다. 그래서 특정 블록이 사용되고 있으면 해당 Block Bitmap 해당 블록의 인덱스가 On(1) 된다.

 

[Block Bitmap 사용 ]

 

- 8개의 블록이 사용되는 예를 나타낸 것으로  블록이 사용되는 곳의 bit 1 표시되어 그룹 내의 블록들의 사용 현황을 알게 되면 추후 새로운 블록 할당시 어느 블록을 할당할지 검색하는 것이 빨라지게  것이다.

- Block Bitmap 하나의 블록 안에 기록되어야 하며, 블록의 크기는 1,2,4 KB 나뉘기 때문에 Block Bitmap 블록의 크기에 따라 8192, 16384, 32768개의 블록 현황을 나타낼  있다.

 

5) Inode

- Ext2 파일시스템에서 파일 객체가 저장되는 곳은 Inode 데이터 구조이며, 모든 Inode 크기는 Super Block 정의되어 있는 크기로 고정된다.

- 모든 파일과 디렉토리들은 각각 1개의 Inode 할당하며, 모든 Inode들은 고유한 주소를 가진다.(Inode 인덱스는 1부터 시작된다.)

- Inode Block Group 내의 Inode Table 저장되고, 앞서 나온 것처럼 Inode Table 위치는 Group Descriptor Table 기록된다.

- 특정 Inode 주소를 안다면 해당 Inode 속한 그룹이 어디인지 계산해   있다.

Blcok Group = (inode – 1)/ INODES_PER_GROUP

n번째 Inode 담겨 있는 Block Group = (n – 1) / INODES_PER_GROUP

 

- INODES_PER_GROUP 값은 Super Block 정의 되어 있고, (Inode-1)  이유는 Inode 인덱스가 1부터 시작하기 때문이다.

- Inode 1전부터 10번까지는 예약되어 있기 때문에 사용할  없으며, 보통 이미 할당되어 있는 상태로 되기 때문에 별도의 작업이 없으면 사용할  없다.

- 1 Inode Super Block 사용하도록 예약되어 있고, 2 Inode 루트 디렉토리로 예약되어 있다.

 

6) Inode Bitmap

- Inode Bitmap Block Bitmap 마찬가지로 1개의 Inode 1개의 Bit 대응되어 해당 블록 그룹이 관리하는 모든 Inode 현황을 나타낼  있다.

- Inode Bitmap 역시 하나의 블록안에 기록되어야 하며, 기능적으로 Block Bitmap 거의 동일하지만, 블록에 대한 정보가 Inode 대한 정보로 바뀔 뿐이다.

- Inode Bitmap Inode 사용되고 있는 현황을 나타내며, 이는 Inode 할당 또는 해제할 때보다 용이하게 쓰여진다.

 

7) Inode Table

- Inode Table 인접한 연속된 블록으로 이루어져 있으며,  블록은 미리 정의된 Inode 개수를 포함한다.

- Inode Table   번째 블록의 번호를 Group Descriptor Table 저장하며, 모든 Inode 크기가 128Byte 동일하다.

- 1024Byte 블록은 Inode 8개를 가지며, 4096Byte 블록은 32개의 Byte 가진다.

 

Count of Inode Table = INODES_PER_GROUP / INODES_PER_BLOCK

 

 

3. 슈퍼블록

1) 슈퍼블록

- Ext2 파일시스템 전체의 레이아웃을 담는 영역으로 1024Byte 내에 있는 작은 영역이지만 Ext2 파일시스템 분석을 위한 중요한 부분이다.

- Ext2 경우 데이터 영역의 기본단위를 블록으로 나타내며 이는 FAT 경우 클러스터와 의미가 비슷하다

- 이때 파일시스템의 블록을 대표하는 블록이 바로 슈퍼 블록이다.

- 블록의 기본 크기(Default Size) 1KB 지정되어 있으며, 파일 시스템 생성시 값을 달리 설정 가능 하다. 그러나 저장장치에서 슈퍼블록이 위치한 곳을 읽기 전까지는 블록 크기를 기본 1KB 보아야 한다.

 

[Block 크기별 Super Block 레이아웃]

 

-  그림과 같이 가장 앞의 1KB Boot Code 사용되는데 이때 블록크기가 1KB 4KB여도 기본적으로 Super Block 읽기 전까지는 1KB 기준이기 때문에 별다른 영향이 없게 된다.

- 블록의 크기는 1024~4096Byte 사이의 값으로 설정 가능하며, 용도에 맞게 블록 크기를 정해야 한다.

- 만약 주로 사용하는 파일들이 1KB 미만이면 블록의 크기를 1KB 해야 단편화를 줄일  있을 것이고, 반대로 주로 사용하는 파일 크기가 4KB 이상이라면 1KB 블록 크기를 설정하면 공간 낭비는 없겠지만 1Block 읽는데 4 이상의 I/O 발생하여 저장장치의 효율이 떨어질 것이다.

 

 그림에서 Boot Code Super Block 바로 옆에 나란히 위치한 것을   있지만 실제 Boot Code Block Group 0 사이에는 예약된 영역이 있게 된다. 따라서 MBR  파티션 테이블에서 파티션 시작 위치를 가져와 1024Byte 오프셋(offset) 두어야 하나 그렇지 않으면 Super Block 아닌 쓰레기 값을 읽어올  있음의 유의해야 한다.

 

2) 슈퍼블록 구조 분석

 

-  1024Byte이며 실제 사용되는 영역은 상위 264Byte 나머지 760 Byte 하위 영역은 예약된 영역이며, 들어있는 것은 쓰레기 값으로 채워져있다.

- 덤프만 가지고  영역이 Super Block 인지 확인 하는 방법은 56번지(0x38) 보게 되면 0xEF53이라고 기록되는데  부분이 Super Block Signature 이다. 다른 데이터 들과 달리 슈퍼 블록의 중간에 위치한 것이 특징이다.

 

[슈퍼 블록 영역을 출력한 ]

 

 

[Super Block 항목]

 

3) 슈퍼 블록의  항목

 

이름

inode count

위치

(Offset)

0

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

파일시스템에서 사용할  있는 최대 inode 개수,  값은 파일시스템 생성  저장장치의 크기에 따라 다르게 설정되며, Block 크기가 4KB 경우 Block  개수를 반으로 나눈 것보다 약간 많다.

 

이름

block count

위치

(Offset)

4

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

파일시스템 내의 전체 블록의 개수로  값은 파티션 테이블에서 섹터의 개수를 '블록크기/512' 값으로 나눈 수와 같다. 1 Block 크기가 4KB라면 섹터의 개수를 8 나눈 값의 소수점을 버린 값이다.

 

이름

reserved block count

위치

(Offset)

8

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

block count 5% 값을 가지며,  값은 아무 대책 없이 파일시스템이  차는 경우를 막기 위해 존재하며, 일종의 예약 영역을 가리키기 위해 있다.

 

이름

free block count

위치

(Offset)

12

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

사용되고 있지 않은(비어 있는) 블록의 개수 이다.

 

이름

free inode count

위치

(Offset)

16

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

사용되고 있지 않은 inode 개수 이다.

 

이름

first data block

위치

(Offset)

20

크기

(Size)

4 Byte

일반적인 

(Value)

0

설명

 번째 블록,  블록 그룹 0 시작되는 블록을 가리킨다.

 

이름

log block size

위치

(Offset)

24

크기

(Size)

4 Byte

일반적인 

(Value)

0 또는 1 또는 2

설명

블록의 크기를 지정하는 값으로

0 : 1KB, 1 : 2KB, 2 : 4KB 의미한다.

블록의 크기는 1,024 좌측으로 log block size 만큼 시프트(shift)해서 구한다.

 

이름

log fragmentation size

위치

(Offset)

28

크기

(Size)

4 Byte

일반적인 

(Value)

0또는 1또는 2

설명

단편화(Fragmentation) 발생  기록되는 크기로서, log block size 같이 0,1,2 표현한다.

 

이름

block per group

위치

(Offset)

32

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

 블록 그룹에 속한 블록 개수

 

이름

fragmentation per group

위치

(Offset)

36

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

 그룹에 속한 단편화된 개수

 

이름

inode per group

위치

(Offset)

40

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

 블록 그룹에 속한 inode 개수

 

이름

mtime

위치

(Offset)

44

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

마지막으로 파일시스템을 마운트한 시간

 

이름

wtime

위치

(Offset)

48

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

마지막으로 슈퍼 블록을 갱신한 시간

 

이름

mount count

위치

(Offset)

52

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

파일시스템을 마운트한 횟수로 이를 기록하는 이유는 사용횟수에 따라 파일시스템을 체크하도록 하기 위해서이다.  시점은 아래 max mount count 가까워지는 경우 체크하며,  count 갱신되는 시점은 e2fsck 실행하는 시점이다.

 

이름

max mount count

위치

(Offset)

54

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

파일 시스템을 마운트할  있는 최대 횟수

 

이름

magic signature

위치

(Offset)

56

크기

(Size)

2 Byte

일반적인 

(Value)

0xEF53

설명

Super Block Magic Signature super Block인지 확인하는 값이다.

 

이름

state

위치

(Offset)

58

크기

(Size)

2 Byte

일반적인 

(Value)

1

설명

파일시스템의 상태를 나타낸다.

 

[파일시스템의 상태 ]

파일시스템 상태

설명

EXT2_VALID_FS

0x0001

정상

EXT2_ERROR_FS

0x0002

에러가 발생한 경우

 

 

이름

error

위치

(Offset)

60

크기

(Size)

2 Byte

일반적인 

(Value)

0

설명

에러 처리를 위한 플래그

 

[Super Block 에러 플래그]

에러 플래그

설명

EXT2_ERRORS_CONTINUE

0

정상

EXT2_ERRORS_RO

1

Read-Only 경우

EXT2_ERRORS_PANIC

2

패닉 상태

EXT2_ERRORS_DEFAULT

EXT2_ERRORS_CONTINUE(0)

기본 (정상)

 

 

이름

minor version

위치

(Offset)

62

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

파일시스템의 마이너(minor) 버전

 

이름

last consistency check time

위치

(Offset)

64

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

e2fsck 등의 프로그램이 파일시스템의 영속성을 위해 마지막으로 체크한 시간

 

이름

check interval

위치

(Offset)

68

크기

(Size)

4 Byte

일반적인 

(Value)

0

설명

s_lastcheck 시간을 비교하여 파일시스템의 영속성을 위한 체크가 필요한 시간에 이르면 파일시스템을 마운트   e2fsck 실행을 요구하는 메시지를 화면에 출력

 

이름

creator os

위치

(Offset)

72

크기

(Size)

4 Byte

일반적인 

(Value)

0

설명

파일시스템을 생성한 운영체제를 나타낸다.

 

[파일시스템을 생성한 OS 식별자]

OS 실별자

설명

EXT2_OS_LINUX

0

Linux

EXT2_OS_HURD

1

GUN Hurd

EXT2_OS_MASIX

2

Masix

EXT2_OS_FREEBSD

3

Free BSD

EXT2_OS_LITES

4

Lites

 

 

이름

major version

위치

(Offset)

76

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

파일시스템의 메이저(major)버전

아래 표의 Dynamic Version Inode 크기가 고정되어 있지 않음을 의미하지만 현재 리눅스 커널은 Dynamic Version 지원하지 않으며 앞으로 사용될 여지는 많지 않다.

 

[파일시스템의 메이저 버전]

OS 실별자

설명

Original Version

0

고정된 Inode 크기

Dynamic Version

1

고정되지 않은 Inode 크기

 

 

이름

UID that can use reserved blocks

위치

(Offset)

80

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

예약된 Block 사용할지의 여부와 관련된 사용자 ID

 

이름

GID that can use reserved blocks

위치

(Offset)

82

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

예약된 Block 사용할지의 여부와 관련된 Group ID

 

이름

first non-reserved inode

위치

(Offset)

84

크기

(Size)

4 Byte

일반적인 

(Value)

11

설명

예약되지 않은 inode  번째 인덱스. 일반적으로 Ext2 파일시스템은 10개의 Inode 예약되 있다.

 

이름

inode structure size

위치

(Offset)

88

크기

(Size)

2 Byte

일반적인 

(Value)

128

설명

Inode 구조체의 크기를 나타냄

 

이름

block group number

위치

(Offset)

90

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

현재의 Super Block 포함하고 있는 Block Group 번호, Super Block 모든 Block Group에서 복사본을 가지며, 해당 Block Group 번호를 의미한다.

또한 Super Block 원본인지 복사본인지 파악을 위해 사용된다.

 

이름

compatible feature flags

위치

(Offset)

92

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

파일시스템에서 지원하는 속성 플래그

 

[파일시스템의 확장속성 지원 가능 플래그]

확장 지원 가능 플래그

설명

EXT2_FEATURE_COMPAT_DIR_PREALLOC

0x0001

단편화를 줄이기 위해 미리 할당된 디렉토리 블록 지원 유무

EXT2_FEATURE_COMPAT_IMAGIC_INODES

0x0002

AFS 서버 Inode 지원 유무

EXT2_FEATURE_COMPAT_HAS_JOURNAL

0x0004

Journaling 지원하는지 유무(Ext3 지원 유무)

EXT2_FEATURE_COMPAT_EXT_ATTR

0x0008

Inode 확장 속성을 기록 하는지의 유무

EXT2_FEATURE_COMPAT_RESIZE_INO

0x0010

파일시스템이 파티션의 크기에 따라 크기를 늘릴  있는지의 유무

EXT2_FEATURE_COMPAT_DIR_INDEX

0x0020

디렉토리에 Hash Index 사용하는지의 유무

EXT2_FEATURE_COMPAT_ANY

0xffffffff

확장 속성을 모두 지원하는 경우

 

 

이름

incompatible feature flags

위치

(Offset)

96

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

파일시스템에서 지원하지 않은 속성 플래그

 

[파일시스템의 확장속성 지원 불가능 플래그]

확장 지원 가능 플래그

설명

EXT2_FEATURE_INCOMPAT_COMPARESSON

0x0001

압축을 지원하지 않는지의 유무

EXT2_FEATURE_INCOMPAT_FILETYPE

0x0002

디렉토리 엔트리가 파일 타입 속성을 포함하지 않는지의 유무

EXT2_FEATURE_INCOMPAT_RECOVER

0x0004

파일시스템의 복구가 필요하지 않은지의 유무

EXT2_FEATURE_INCOMPAT_JOURNAL_DEV

0x0008

파일시스템이 journaling 지원하지 않는 장치를 사용하는지의 유무

EXT2_FEATURE_INCOMPAT_META_BG

0x0010

파일시스템이 메타 Block Group 지원하지 않는지의 유무

EXT2_FEATURE_INCOMPAT_ANY

0x0020

위의 모든 속성을 지원하지 않는지의 유무

 

 

이름

read-only feature flags

위치

(Offset)

100

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

Super Block에서 읽기전용(Read Only) 속성인 플래그

 

[파일시스템의 읽기 전용 플래그]

확장 지원 가능 플래그

설명

EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER

0x0001

Super Block Group Descriptor Table 산재해 있는지 관련 플래그

EXT2_FEATURE_RO_COMPAT_LARGE_FILE

0x0002

대용량 파일 지원 유무와 관련된 플래그

EXT2_FEATURE_RO_COMPAT_BTREE_DIR

0x0004

디렉토리 구조가 B-TREE 구조로 되어 있는지와 관련된 플래그

EXT2_FEATURE_RO_COMPAT_ANY

0xffffffff

 모든 속성을 포함하는 플래그

 

이름

UUID(FileSystem ID)

위치

(Offset)

104

크기

(Size)

16 Byte

일반적인 

(Value)

가변적

설명

파일시스템의 식별자

 

이름

volume name

위치

(Offset)

120

크기

(Size)

16 Byte

일반적인 

(Value)

가변적

설명

볼륨명

 

이름

last mounted path

위치

(Offset)

136

크기

(Size)

64 Byte

일반적인 

(Value)

가변적

설명

마지막으로 마운트되었던 지점의 경로

 

이름

algorithm usage bitmap

위치

(Offset)

200

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

비트맵 저장과 관련된 알고리즘, 압축과 관련된 알고리즘을 의미

 

이름

preallocated blocks count

위치

(Offset)

204

크기

(Size)

1 Byte

일반적인 

(Value)

가변적

설명

파일을 기록하기 위해 미리 할당된 블록의 개수

 

이름

preallocated dir blocks count

위치

(Offset)

205

크기

(Size)

1 Byte

일반적인 

(Value)

가변적

설명

디렉토리를을 기록하기 위해 미리 할당된 블록의 개수

 

이름

padding

위치

(Offset)

206

크기

(Size)

2Byte

일반적인 

(Value)

가변적

설명

4Byte 정렬(alignment) 위한 패딩

 

이름

journal UUID

위치

(Offset)

208

크기

(Size)

16 Byte

일반적인 

(Value)

가변적

설명

Journal ID

 

이름

journal inode number

위치

(Offset)

224

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

Journal Inode 번호

 

이름

journal device

위치

(Offset)

228

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

Journalling 가능 장치

 

이름

orphan inode list

위치

(Offset)

232

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

부모 inode 잃어버린 inode 리스의 head 포인터

 

이름

hash seed

위치

(Offset)

236

크기

(Size)

16 Byte

일반적인 

(Value)

가변적

설명

해싱 알고리즘을 위해 사용되는 기본 해시 코드

 

이름

defined hash version

위치

(Offset)

252

크기

(Size)

1 Byte

일반적인 

(Value)

가변적

설명

해시 코드 버전

 

이름

padding

위치

(Offset)

253

크기

(Size)

1 Byte

일반적인 

(Value)

가변적

설명

1Byte 패딩 영역

 

이름

padding

위치

(Offset)

254

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

2Byte 패딩 영역

 

이름

default mount option

위치

(Offset)

256

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

마운트 옵션

 

이름

first meta block

위치

(Offset)

260

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

 번째 데이터 블록 번호

 

이름

reserved

위치

(Offset)

264

크기

(Size)

760 Byte

일반적인 

(Value)

가변적

설명

1024Byte 채우기 위한 패딩 영역

 

 

4. 그룹 디스크립터 테이블

 

1) 그룹 디스크립터 테이블

- Super Block 바로 다음에 위치한 데이터 구조로, 모든 블록 그룹에 대한 Descriptor 가지고 있기 때문에 Descriptor Table 또는 Descriptor List 부른다.

- Super Block 마찬가지로 파일시스템 내의 모든 블록 그룹들이 Group Descriptor Table 복사본을 가지고 있다.

 

2) 그룹 디스크립터 테이블 구조 분석

[Group Descriptor Table 영역을 출력한 ]

 

-  덤프한 그림은, Super Block 바로 다음 블록에서부터 위치한 Group Descriptor Table 덤프 영역을 출력한 것으로 512Byte 덤프 하였지만 실제 Group Descriptor 개수는 정해져 있지 않다. 따라서 Super Block에서 간단한 연산을 통해 파티션 내에 존재하는 Block Group 개수를 파악한   만큼의 데이터를 파일시스템에서 읽어와야 한다.

 

[Group Descriptor 항목]

 

3) 그룹 디스크립터 테이블의  항목

 

이름

starting block address of block bitmap

위치

(Offset)

0

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

Block Bitmap 블록 번호로  값이 필요한 이유는 Group Descriptor Table 크기가 고정되어 있지 않고 바로 다음에 위치하는 Block Bitmap 위치를 나타내기 위함이다.

만약 Group Descriptor Table 1Block 안에 모드 들어간다면  항목의 값은 2 된다.

 

이름

starting block address of inode bitmap

위치

(Offset)

4

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

Inode Bitmap 블록 번호로 bg_inode_bitmap 항목도 bg_block_bitmap 항목과 마찬가지로 블록의 위치를 나타내기 위한 값으로, Block Bitmap 고정적으로 1Block 사용하므로  항목의 값은 보통 "block bitmap 번호 +1" 된다.

위의 덤프를 출력한 것을 보게 되면  번째 항목과 2번째 항목간에 1 차이를   있다.

 

이름

starting block address of inode table

위치

(Offset)

8

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

Inode Table 블록 번호로 실제 Inode 데이터가 다민 블록이 시작되는 블록의 번호이다.

 

이름

free blocks count

위치

(Offset)

12

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

Block Group 내의 비어있는 블록 수로 블록은 실질적으로 데이터가 저장되는 기본 단위이기 때문에  값은 데이터가 저장될 그룹을 결정하는  중요한 요소가 된다.

(가능하면 비어있는 블록이 많은 블록 그룹이 우선순위로 결정된다.)

 

이름

free inode count

위치

(Offset)

14

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

블록 그룹 내의 비어있는 Inode . 사용 가능한 Inode 개수가 정해져 있으므로 비어있는 Inode 개수도 따로 존재한다. 그러나 데이터가 저장되는 기본 단위가 블록이므로 Inode 부족할만한 상황은 걱정하지 않아도 되는 것이다.

 

이름

directories count

위치

(Offset)

16

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

Block Group 내에 생성된 디렉토리 . 디렉토리 수는 데이터를 저장할  저장할 그룹을 결정하는 주요 요소  하나이다.

일반적으로 특정 디렉토리 하위 파일들을 부모 디렉토리와 같은 그룹에 위치하도록 알고리즘이 짜여지기 때문에 디렉토리가 있다는 것은 그만한 여유 공간을 필요로 하나는 것이다. 이러한 이유로 디렉토리가 많을수록 우선 순위가 낮아지게 되는 것이다.

 

이름

padding

위치

(Offset)

18

크기

(Size)

2 Byte

일반적인 

(Value)

가변적

설명

4Byte 단위 정렬(alignment) 위한 패딩(padding) 영역이다.

 

이름

reserved

위치

(Offset)

20

크기

(Size)

12 Byte

일반적인 

(Value)

가변적

설명

예약된 영역. 32 Byte 맞추기 위해 12Byte 패딩하기 위한 영역이다.

내용

1.FAT 파일시스템으로 포맷하기

1) 파일 시스템 선택

-  저장장치를 포맷을 할땐 어떤 파일시스템을 사용할  결정 해야하고, FAT12, FAT16, FAT32 결정하는 요소  가장 중요한 것은 클러스터의 개수이며, 각각의 파일 시스템들은 그에 맞는 클러스터 개수의 범위가 정해져 있다.

 

[파일시스템의 클러스터 개수 범위]

파일 시스템

클러스터 개수 범위

FAT 12

~ 4084

FAT 16

4085 ~ 65524

FAT 32 

65525 ~

 

- FAT12 경우 클러스터 개수가 4084 넘는 것은 없고, 4085~ 65524사이의 클러스터 개수를 가진 FAT 16파일시스템도 없다.

- 만약  규칙을 어길 경우 Windows 볼륨을 제대로 인식하지 않는다.

 

클러스터개수를 1,2,6,10 혹은 16 모자라거나 많게 FAT 형태를 계산하는 코드들도 있기 때문에 기존 FAT 코드들과 최대한 호환되는 FAT 볼륨을 생성을 위해선 4085 65525 근처보다는 최소 16클러스터 이상 떨어뜨리는 것이 좋다.

 

- FAT 32파일 시스템의 경우 최소 32MB 이상은 되어야 하게 된다. 최소 클러스터 65526 X 512byte = 33,548,800Byte ( 31.9MB)

- FAT 16 경우 4085 X 512 = 2,091,520Byte( 1.9MB)  최소크기가 매우 작다는    있다.

- 32MB 이하는 FAT 16 파일시스템밖에 선택   없을 것이고, 2GB 넘어가면 FAT 32파일시스템 밖에 선택할  없지만, 32MB ~ 2GB 사이라면 FAT16 FAT32 파일시스템 중에서 선택할  있게 된다.

- 다시 한번 볼륨 용량에 따른 클러스터 크기를 알아보자

 

[Windows 2000 기준]

볼륨 크기

FAT16 클러스터 크기

FAT32 클러스터 크기

NTFS 클러스터 크기

16MB ~ 32MB

512Byte

지원  

512Byte

32MB ~ 63MB

1KB

512Byte

512Byte

64MB ~ 128MB

2KB

1KB

512Byte

128MB ~ 256MB

4KB

2KB

512Byte

256MB ~ 512MB

8KB

4KB

512Byte

512MB ~ 1GB

16KB

4KB

1KB

1GB ~ 2GB

32KB

4KB

2KB

2GB ~ 4GB

64KB()

4KB

4KB

4GB ~ 8GB

지원  

4KB

4KB

8GB ~ 16GB

지원  

8KB

4KB

16GB ~ 32GB

지원  

16KB

4KB

32GB ~ 2TB

지원  

인식 가능 ()

4KB

 

2) FAT 영역 크기 구하기

- Microsoft에서 제공하는 공식문서에 따른 계산 법이지만 정확한 계산을 해주진 않는다.

- 때때로 FAT16에서 2섹터, FAT 32에서 8섹터를 잡는 경우가 있으나 FAT 영역의 크기를 작게 잡는 일은 없다.

- 섹터가  개가 낭비된다는 것을 제외하곤 FAT 영역을 약간 크게 잡는다고 문제될 것은 없다.

RootDirSecCnt = ((RootEntCnt * 32) + (BytesPerSec -1)) / BytesPerSec;

TmpVal1 = VolumeSize – (ReservedSecCnt + RootDirSecCt);

TmpVal2 = (256 * SecPerClus) + NumFATs;

if(FATType == FAT32)

    TmpVal2 = TmpVal2 / 2;

FATSize = (TmpVal1 + (TmpVal2 – 1)) / TmpVal2;

 

3) 부트 레코드 생성

- 클러스터 크기와 개수가 결정되었다면 부트 레코드를 생성한다.

- FAT16 FAT32 파일시스템의 부트레코드 형태에 맞게 생성한다.

 

4) 예약된 영역 생성

- 예약된 영역으로 지정된 섹터들을 전부 '0'으로 초기화 시킨다.

- FAT32 경우 예약된 영역에 FSInfo 구조체를 담기 때문에 이를 생성 한다.

 

5) FAT영역 생성

- 크기를 구한 FAT 영역을 모든 클러스터가 비었음을 의미하는 값을 갖도록 전부 '0'으로 초기화 시킨다.

- FAT32 파일시스템이라면 Cluster 0 Entry : 0x0FFFFFF8, Cluster 1 Entry : 0x0FFFFFFF 바꾼다.

- FAT16파일시스템이라면 Cluster 0 Entry : 0xFFF8, Cluster 2 Entry : 0xFFFF 바꾼다.

- 클러스터의 개수가 섹터단위로 정확히 나눠 떨어지는 경우는 거의 없기 때문에 마지막 섹터의 뒷부분은 클러스터가 없는  영역으로  것이고  부분도 전부 '0'으로 초기화시켜야 한다.

 

6)루트 디렉토리 생성

- 루트 디렉토리 영역에 해당하는 섹터를 전부 0으로 초기화 시킨다.

- FAT32 경우 루트 디렉토리에 섹터를 전부 0으로 초기화 시키면서 해당 클러스터의 Entry 값을 0x0FFFFFFF 바꾸어 해당 클러스터는 사용 중이라고 표시한다.

- 루트 디렉토리 생성시 볼륨 레이블을 지정하고 싶을 경우 초기화  Label Directory Entry 집어넣는다.

 

 

2. 파일시스템 알아내기

- 특정 볼륨의 파일 시스템을 알아내기 위해서는 클러스터의 개수를 얻어서 분석하는 방법이 가장 정확한 방법이다. 가장 위의 표를 참고하자.

- 클러스터 개수 등의 정보를 얻기 위해선 부트 레코드를 분석해야 하는데 FAT32 FAT16 부트 레코드 형태가 다르기 때문에  부분을 정확히 읽기 위한 조사 방법을 알아보자

FAT32 경우 부트 레코드의 FATSize 16값은 반드시 0이다.

FAT32 경우 Root Dir Entry Count 항목 값이 반드시 0 이다.

- 단순히 OEM Name 항목이나 File System Type 읽는 것은 매우 위험하다. 단순한 문자열일  속성이 아니다.

 

 

3. 다음 클러스터 번호 알아내어 섹터 찾기

- FAT 테이블을 조사하여 다음 클러스터의 위치 등을 알아야 한다.

- n 클러스터 FAT Entry   번째 FAT 섹터에 위치하는지,  번째 Entry n  클러스터의 Entry인지 알아내는 것이 중요할 것이다.

- 아래 소스에서 ThisFATSecNum  n 번째 FAT Entry 섹터 번호이고, ThisFATEntOffet 섹터 안에서 n번째 Entry 위치 값이다.

U32    FATOffset, tmpFATSecNum, ThisFATSecNum, ThisFATEntOffset;

 

if(FATType == FAT16)

    FATOffset = N * 2;

else if(FATType = FAT32)

    FATOffset = N *4;

 

tmpFATSecNum = FATOffset / BytePerSec;

ThisFATSecNum = VolBeginSec + ReservdSecCnt + tmpFATSecNum;

 

if(FATType == FAT16)

    ThisFATEntOffset = N - (tmpFATSecNum * 256);

else if

    ThisFATEntOffset = N - (tmpFATSecNum * 128);

 

- n번째 FAT Entry 안의 값을 얻어야 한다. FAT Table다음 클러스터 번호를 얻어보자.

- 아래 소스에서 FAT16ClusEntryVal, FAT32ClusEntryVal 값이 n 클러스터와 연결된 다음 클러스터 번호가 된다.

 

U16 FAT16FATSec[256], FAT16ClusEntryVal;

U32 FAT32FATSec[128], FAT32ClusEntryVal;

 

if(FATType == FAT16){

    HDD_read(Drive, ThisFATSecNum, 1, (char*)FAT16FATSec);

    FAT16ClusEntryVal = FAT16FATSec[ThisFATEntOffset];

}else{

    HDD_read(Drive, ThisFATSecNum, 1, (char*)FAT32FATSec);

    FAT32ClusEntryVal = FAT32FATSec[ThisFATEntOffset] & 0x0FFFFFFF;

 

- 최종 섹터 주소로 변환한  해당 섹터를 읽어야 한다.

- 클러스터 번호를 섹터 주소로 변환하는 소스를 알아보자

U32 FirstSecOfThisClus;

if(FATTYPE == FAT16)

    FirstSecOfThisClus = ((FAT16ClusEntryVal - 2) * SecPerClus) + FirstDataSector;

else

    FirstSecOfThisClus = ((FAT32ClusEntryVal - 2) * SecPerClus) + FirstDataSector;

-  소스에서 ClusEntrVal - 2하는 이유는 클러스터의 0번과 1번은 사용하지 데이터 영역의  번째 클러스터가 2번이기 때문이다.

 

 

4. 디렉토리 엔트리 추가

- 새로운 파일이나 하위 디렉토리 생성시  볼륨에 기록하는 작업 외에도 새로운 디렉토리 엔트리를 생성하고, 정보를 나타내야 한다.

[ "File"이라는 폴더에 "Forensic.txt"  추가하는 경우 ]

1) "File" 디렉토리 내에 "Forensic.txt" 파일이 있는지 조사

2) 비어있는 공간을 찾기 위해 Name[0] 값이 0 나올 때까지 "File" 디렉토리의 내용 조사

3) 만약 디렉토리 끝까지 조사하였을  비어 있는 엔트리가 없다면 새로운 클러스터를 할당 받아 디렉토리 크기를 늘린다.

4) 새로운 디렉토리 엔트리를 추가하고, Long File Name 필요하면 Long File Name Entry 추가한다.

 

- 디렉토리를 끝까지 조사 했는데 비어 있는 엔트리가 없을 경우 새로 클러스터를 할당 받는데 새로 할당 받지 않고 Name[0xE5] 경우  디렉토리 엔트리를 덮어쓰는 방법도 있다. 그러나 이럴 경우 삭제된 파일의 정보가 사라져 삭제된 파일의 복구가 어려워 진다.

 

FAT 16 파일시스템의 경우 루트 디렉토리가  찼다면 클러스터를 할당하는 방법으로 루트 디렉토리의 크기를 늘릴  없다. FAT 16에서 루트 디렉토리 크기는 고정되어 있다.

 

 

5. 하위 디렉토리 생성

- FAT 파일시스템에서 루트 디렉토리를 제외한 모든 디렉토리들은 자신의 상위 디렉토리를 가지고 있다.

- 특수한 디렉토리 엔트리인 '.' '..' 디렉토리 엔트리를 통해 자신의 상위 디렉토리 위치를 기억한다.

- 루트 디렉토리를 제외한 모든 디렉토리들은 생성 시에 '.' '..' 디렉토리 엔트리를  번째와 두번째 엔트리에 필수적으로 생성해 주어야 하며, '.' 디렉토리 엔트리는 자신, '..' 디렉토리 엔트리는 상위 디렉토리를 가리키는 디렉토리 엔트리이다.

- 첫번째 클러스터 값을 해당 디렉토리 엔트리 First Cluster항목 값에 저장한다는 의미가 가리킨다는 말이다.

 

[디렉토리 계층의 ]

 

- 하위 디렉토리 생성 방법

1) FAT Table 조사하여  클러스터의 번호를 얻는다.

2) 찾은  클러스터 영역을 모두0으로초기화 한다.

3) 초기화된 클러스터에  번째로 자신을 가리키는 '.' 디렉토리 엔트리를 생성한다.

4)  번째 엔트리에 자신의 상위 디렉토리를 가리키는 '..' 디렉토리 엔트리를 생성한다.

5) FAT Table 해당 클러스터 FAT Entry 값을 EOC 바꾸어 준다.

6) 상위 디렉토리에 방금 생성한 디렉토리를 가리키는 디렉토리 엔트리를 추가한다.

 

- 디렉토리를 생성할 경우 디렉토리 엔트리 들은 FileSize 항목 값을 0으로 해야 하며, 따라서 디렉토리의 크기를 조사할 때는 파일처럼 바로 크기를   없고, 디렉토리 크기 조사  경우 FAT Entry 항목의 EOC 값을 이용해야 한다.

 

6. 새로운 파일 생성

- 파일 생성도 하위 디렉토리 생성처럼 비슷하다.

1) FAT Table 조사하여  클러스터의 번호를 얻어낸다.

2) 찾아낸  클러스터 영역에 파일의 내용을 적는다.

3) 클러스터에 파일의 내용을  적었다면  다른  클러스터를 찾기

4) 마지막으로 파일 내용을 적은 클러스터의 FAT Entry 찾아낸  클러스터 번호를 적는다.

5) 파일의 내용을  적을 때까지 1~4 작업 진행

6) 마지막으로 사용된 클러스터의 FAT Entry 값에 EOC 적는다.

7) 상위 디렉토리에 새로 생성한 파일의 정보를 담은 디렉토리 엔트리를 추가한다.

 

- 새로 생성한 파일의 정보를 담은 디렉토리 엔트리 생성시 First Cluster 항목에 파일의 내용을 담은  번째 클러스터 번호를 적어주고, 파일의 크기를 File Size 기록 해야 한다.

- 파일을 새로 생성할 경우 동시에 여러 클러스터를 사용하는 경우가 많은데 이럴 경우 비어있는 클러스터를 계속 찾아 파일의 내용을 쓰되, FAT 테이블에 파일의 클러스터 연결고리를  적어줘야 한다.

 

 

7. 파일 또는 디렉토리 삭제

1) 해당 디렉토리 엔트리의 Name[0] 값을 0xE5 바꾼다.

2) 해당 파일이 Long File Name 가지고 있을 경우 모든 Long File Name Entry Order 항목 값을 0xE5 바꾼다.

3) 해당 파일에 연결되어 있던 클러스터들의 FAT Entry 값을 전부 Free Cluster 값으로 바꾸어 준다.

 

8. 주의 사항

1) 디렉토리  파일 개수 제한

- 파일 내의 디렉토리 개수 제한이 없지만 FAT 코드들은 디렉토리 엔트리 개수를 U16(unsigned Short)형으로 제한하는 경우가 많다.

  엔트리의 개수가 65,535 보다 적어야 한다. 그러나  디렉토리를 생성할 경우 파일 생성   마다 생성할 파일의 이름이 존재하는지

모든 Directory Entry  조사해야 하기 때문에 효율적이지 못하다.

 

2) 파일의 최대 크기

- Directory Entry 항목으로 FileSize 항목은 4바이트이며, 최대 4GB(232)까지 하나의 파일로 가질  있다.

 

3) 마지막 클러스터의 크기 FAT Entry 위치

- 볼륨 마지막 FAT Entry위치를 FAT 영역의 마지막 섹터의 가장 뒷부분의 위치한 FAT Entry 단정 짓는 경우가 많은데 문제점이 발생할  있다. 볼륨의 마지막 FAT Entry인지 아닌지는 FAT Entry 번호로 판단을 해야 한다.

FAT16 파일시스템에서 볼륨의  클러스터 개수가 5000 라면

FAT 영역은  20 섹터이며 136번째 FAT Entry 클러스터 5000번과 대응 하는 볼륨의 마지막 FAT Entry 되며  뒤는 의미가 없다.

FAT 16 경우 클러스터 주소 2bit 나타냄

 1섹터당 주소 (512byte / 2bit) = 256개씩 들어감

256 x 19 + 136 = 5000

20번째 섹터의 136bit까지 사용하고 나머지는 376bit 사용  .

 부분을 FAT Entry 단정하면 안됨

 

9. 실습

- USB 같은 곳을 FAT 16으로 포맷을 하자.

 

포맷을   TEST.TXT 라는 파일을 FAT16으로 포맷한 드라이브에 넣어놓도록 하자.

 

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 _File_struct{    //파일 구조체 선언

    U32    curCluster;

    U32    FileSize;

    U32    FileRestByte;

    U32    ClusRestByte;

    U8*    ClusBuf;

}FileStruct;

 

 

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;

#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;

 

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

U32 read_file(FileStruct* pFile, U32 read_size, U8* buf);

U32 open_file(char* FileName, char* FileExtender, DirEntry* pDir, FileStruct* pFile);

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

U32 clus_convTo_sec(U32 clusterNum);

U32 get_next_cluster(U32 curCluster);

 

VolStruct gVol;

 

int main(void){

    U8        buf[512];

    U8    *    pRootBuf;

    U32    readBytes;

    FileStruct readFile;

    FILE*        outFile;

 

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

    gVol.VolBeginSec = 0x87; //부트 레코드 섹터

 

    //저장장치로부터 부트 레코드 섹터를 읽어 buf에 담기.

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

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

        return 1;

    }

 

    //읽은 buf를 처리한 뒤, gVol 변수에 담는다. 파일시스템이 맞지 않으면 에러

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

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

        return 1;

    }

 

    //루트 디렉토리를 읽어 메모리에 할당.

    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;

    }

 

    //루트 디렉토리에서 test.txt파일을 찾아 readFile 구조체에 정보를 담는다

    if(open_file("TEST","TXT",(DirEntry*)pRootBuf, &readFile) != 0){

        printf("File Find Error\n");

        return 1;

    }

    

    //C드라이브에 test.txt파일을 생성할 준비를 한다.

    fopen_s(&outFile,"c:\\test.txt","wb");

    if(outFile == NULL){

        printf("Can't Create File \n");

        return 1;

    }

 

    //저장장치에 있는 test.txt 파일로부터 512Byte를 읽어 더 이상 읽을게 없을 때 까지 C로 복사

    do{

        readBytes = read_file(&readFile, 512, buf);

        fwrite(buf,readBytes,1,outFile);

    }while( readBytes!=0);

 

    fclose(outFile);

 

    return 0;

}

 

U32 read_file(FileStruct* pFile, U32 read_size, U8* buf) {

    U32        bkFileRestByte = 0;

    U32        curClusStartSec = 0;

    U32        ThisClusOffset = 0;

    U32        ThisReadBytes = 0;

 

    //파일을 어디까지 읽었는지 저장

    bkFileRestByte = pFile->FileRestByte;

 

    while(1){     //클러스터를 더 읽을 바이트가 남았는지 조사, 더 이상 없으면 다음 클러스터를 읽음

        if(pFile->ClusRestByte ==0){

 

            curClusStartSec = clus_convTo_sec(pFile->curCluster);

 

            if(HDD_read(gVol.Drive, curClusStartSec, gVol.SecPerClus, pFile->ClusBuf) == 0)

                return 0;

            pFile->curCluster = get_next_cluster(pFile->curCluster);

            pFile->ClusRestByte = gVol.ClusterSize;

        }

 

        //클러스터의 어느 부분을 읽어야 하는지 찾아 변수(ThisClusOffset)에 저장

        ThisClusOffset = gVol.ClusterSize - pFile->ClusRestByte;

 

        //파일을 읽을 크기인 read_size 변수의 크기가 남은 파일 크기보다 큰 경우 남은 크기 저장

        read_size = (read_size >= pFile->FileRestByte)? pFile->FileRestByte : read_size;

        ThisReadBytes = (read_size >= pFile->ClusRestByte)? pFile->ClusRestByte : read_size;

 

        //읽은 바이트 수가 0이 아니면 더 남아있기 때문에 계속 일어 내용을 옴겨 담음

        if(ThisReadBytes != 0){

            memcpy(buf, &pFile->ClusBuf[ThisClusOffset], ThisReadBytes);

            pFile->ClusRestByte -= ThisReadBytes;

            pFile->FileRestByte -= ThisReadBytes;

            read_size -= ThisReadBytes;

        }else

        //읽은 바이트 수가 0이면 다 읽었기 때문에 리턴, 실제로 몇 바이트 읽었는지 리턴

            return bkFileRestByte - pFile->FileRestByte;

    }

}

 

U32 open_file(char *FileName, char* FileExtender, DirEntry* pDir, FileStruct* pFile)

{

    U32 i;

    char compFile[11];

 

    //사용자가 넘겨준 파일명과 확장자를 FAT 파일 시스템 형태로 변환

    memset(compFile, 0x20, sizeof(compFile));

    memcpy(&compFile[0], FileName, strlen(FileName));

    memcpy(&compFile[8], FileExtender, strlen(FileExtender));

 

    //찾는 파일이 있는지 계속 조사. Name[0]이 0을 만나면 파일을 못찾고 Entry 검색한 경우.

    //0xE5의 경우 삭제된 Entry므로 건너 뛴다.

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

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

        case 0x00 : break;

        case 0xE5 : continue;

        }

 

    //현재 Directory Entry의 Attrivute 값이 0x0F이면 Long File Name Entry이기 때문에 조사

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

            //찾아낸 Directory Entry Name 항목과 사용자가 검색한 파일명 비교

            //일치할 경우 넘어온 파일 포인터에 해당하는 파일 정보를 담기

            if( (memcmp(pDir[i].Name, compFile, sizeof(compFile))) ==0){

                pFile->curCluster = pDir[i].FstClusLow | pDir[i].FstClusHi << 16;

                pFile->FileSize = pDir[i].FileSize;

                pFile->FileRestByte = pFile->FileSize;

                pFile->ClusRestByte = 0;

                pFile->ClusBuf = (U8*)malloc(gVol.ClusterSize);

 

                return 0;

            }

        }

    }

    return 1;

}

 

U32 get_next_cluster(U32 curCluster){

    U16 FATtable[256];

    U32 ThisFATSecNum, ThisFATEntOffset, tmpFATNum;

 

    //curCluster가 몇 번째 FAT 섹터인지 확인. FAT32라면 128로 나눔

    tmpFATNum = curCluster / 256;

    //실제 저장장치 상에서 FAT Table의 위치를 저장

    ThisFATSecNum = gVol.FATStartSec + tmpFATNum;

    //FAT Table의 섹터 내에서 curCluster FAT Entry 위치를 구한다. FAT32면 128을 곱한다.

    ThisFATEntOffset = curCluster - (tmpFATNum * 256);

 

    //FAT Table의 위치를 읽어 해당 FAT Entry 값을 리턴

    HDD_read(gVol.Drive, ThisFATSecNum, 1, (U8*)FATtable);

 

    return FATtable[ThisFATEntOffset];

}

 

U32 clus_convTo_sec(U32 clusterNum){

    //클러스터번호를 실제 섹터 주소 값으로 변환, 0,1번 클러스터는 사용하지 않고 Cluster 2번부터 사용

    return ((clusterNum -2 ) * gVol.SecPerClus) + gVol.FirstDataSec;

}

 

    //DATA 읽기

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 확인을 해보았다.

 

[실행 화면]

그냥 컴파일  아무 것도 표시 되지 않으면 정상적으로 진행  것이다.

 

[FAT 16으로 포맷한 USB 있던 TEST.TXT]

 

[컴파일  복사된 C:\\TEST.TXT 파일]

열어보면 같은 내용이 있는걸   있다.

 

내용

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 확인을 해보았다.

 

[실행화면]

 

 

내용

1. 파일시스템 소개

1) 파일시스템의 탄생

[ FAT파일 시스템 변화 ]

 

- FAT 12 플로피디스크를 관리하는데 이용하기 위해 구현하게 되었다.

- Tim Paterson  QDOS 운영체제를 개발하면서 FAT 파일시스템으로 선택하면서 PC환경에 쓰이게 된다.

- FAT 경우 매우 단순한 구조를 가지고 있었고, 조금만 수정하면 32MB 이상 관리도   있을 거라 생각 했는데 PC환경에서도 충분한 용량으로 생각 했다.

[Thinks~]

IBM 개인용 컴퓨터 성능이 16bit CPU 충분하다고 생각 하는 바람에 32bit CPU 출시를 빼앗겼고, Microsoft 메모리의 크기를 640KB 충분하다고 생각해서 MS-DOS 사용한 유저들을 고통으로 몰아 넣게 .

- QDOS 나오면서 FAT파일시스템이 소개 되며, IBM Microsoft 넘어가면서 PC-DOS, MS-DOS 바꾸고, x86 PC 운영체제로 탐재되고, IMB x86 PC 엄청난 성공을 거두면서 표준처럼 자리잡고, 다른 PC 메이커들도 호환기존으로 만들어 내면서 MS-DOS 대중적인 OS 되었다. 따라서 자연스레 FAT 파일시스템이 가장 많이 쓰이는 파일시스템으로 자리 잡게 되었다.

 

2) FAT 12

- 80년에 나온 FAT 파일시스템의 최초 버전은 다른 버전과 구분하기 위해 FAT12라고 불리며 플로피디스크 파일저장을 위해 개발되었다.

- 당시 PC HDD 없고 5.25인치 플로피디스크가 대세였던 시절이였고, 최초로 발표된 FAT 12구조가 매우 단순해서, 디렉토리(Directory)라는 개념도 없었다.

- MS-DOS 2.0 나오면서 FAT12 계층형 디렉토리가 지원되고, 최근의 FAT파일시스템의 기본 구조가 갖추게 된다.

 

3) FAT 16

- 개인용 PC HDD 장착하는 경우가 많아지면서 HDD에서 사용할 파일시스템의 필요성이 증가고, FAT 16버전의 경우 비로서 HDD에사용할 만한 파일시스템이 되었다.

- 구조적으로 FAT 12 거의 동일하지만 FAT 16에서는 클러스터를 표현하는 비트수가 12개에서 16개로 늘어나면서 이론적으로 최대 216 65,535개의 클러스터를 표현할  있게 되었다.

- 이렇게 클러스터 크기를 32KB  경우 최대 2GB까지 표현   있었다. 이때는 평생 써도 남을 정도로 충분하다고 판단 했었다.

 

4) VFAT(Virtual FAT)

- Windows 95버전으 나오면서 FAT파일시스템의 성능  기능을 향상 시켰다.

- VFAT 또는 FAST FAT라고 불러야 하지만 사람들은 FAT 파일시스템이라 불렀다.

- 32Bit 보호모드(Protected Mode) 적합하게 코드  작성하여 성능향상을 시켰고, 독점모드(Exclusive Mode) 추가하여 동시에 여러 프로그램을 같은 파일을 접근할 경우에 대비하였다. 하지만 이는 파일시스템 구조라기 보단 OS 파일 시스템 처리 성능이 개선된 것이다.

- VFAT 경우 LFNs(Long File Names) 지원하여 파일명을 최대 255까지 적을  있게 되었다. 이전 버전의 경우 파일명이 최대 8byte 확장명은 최대 3byte 고작 이였고, 파일명과 확장명은 모두 대문자만 가능 했었다. LFNs 경우 이전 버전의 DOS와도 호환성을 가지면서 파일명의 길에 제한으로부터 자유로울  있게 되었다.

- 파일명의 최대 글자수는 늘어났지만 최대 용량은 여전히 2GB정도가 최대였다.

 

5) FAT 32

- 1996 FAT32버전이 출시되었고, VFAT 기반으로 수정하여 클러스터를 표현하는 bit 32개로 늘렸으나 최상위 4bit 예약 영역으로 사용되지 않고,  28bit 이용하여 클러스터를 표현한다.

- 이는 클러스터 크기를 16KB 하면 최대 4TB까지 가능한 용량이다.

- Windows 경우 FAT32 2TB까지 인식   있지만 여러 가지 이유로 최대용량을 32GB 제한하고 있다.

- 만약 Windows에서 32GB 이상되는 저장 장치를 사용하고 싶다면 NTFS 사용해야 한다.

 

6) FAT 파일시스템들의 비교

[FAT 파일 시스템 비교]

구분

FAT12

FAT16

FAT32

사용 용도

플로피 디스크용

저용량 하드디스크

고용량 하드디스크

클러스터 표현 비트 

12 bit

16bit

32bit(28bit 사용)

최대 클러스터 개수

4,084

65,524

 228 

최대 볼륨 크기

16MB

2GB

2TB

파일의 최대 크기

볼륨 크기 만큼

볼륨 크기 만큼

4GB

디렉토리당 최대 파일 개수

X

65,535

65,535ro

루트 디렉토리의 파일 개수 제한

제한 있음

제한 있음

제한 없음

[클러스터 개수의 경우 Microsoft에서 발표한 문서에서 표시한 내용이다.]

 

 

7) 클러스터 크기와 슬랙(Slack) 문제

- 클러스터 크기와 볼륨 크기와 어떤 관계가 있는지, 클러스터 크기에 따른 장단점이 무엇인지 파악 해보자.

[Windows 2000 기준]

볼륨 크기

FAT16 클러스터 크기

FAT32 클러스터 크기

NTFS 클러스터 크기

16MB ~ 32MB

512Byte

지원  

512Byte

32MB ~ 63MB

1KB

512Byte

512Byte

64MB ~ 128MB

2KB

1KB

512Byte

128MB ~ 256MB

4KB

2KB

512Byte

256MB ~ 512MB

8KB

4KB

512Byte

512MB ~ 1GB

16KB

4KB

1KB

1GB ~ 2GB

32KB

4KB

2KB

2GB ~ 4GB

64KB()

4KB

4KB

4GB ~ 8GB

지원  

4KB

4KB

8GB ~ 16GB

지원  

8KB

4KB

16GB ~ 32GB

지원  

16KB

4KB

32GB ~ 2TB

지원  

인식 가능 ()

4KB

 

  1. NT, 2000, xp에서 클러스터 크기를 64KB 해서 2~4GB 볼륨을 16으로 포맷 가능하지만 일부 응용프로그램에서는 64KB 클러스터 볼륨을 인식 못하는 경우가 있으니 2GB넘는 볼륨은 FAT16 아닌 다른 파일시스템을 사용하길 권장한다.

  2. Windows 32GB 넘는 볼륨에 FAT32 이용해서 포맷하는 것을 지원하지 않지만 다른 장치가 포맷한 32GB 넘는 FAT32 볼륨은 인식   있다.

 

[클러스터 크기에 따른 장단점]

구분

클러스터 크기가 작을 

클러스터 크기가  

장점

버려지는 용량이 적다

FAT 영역이 작다.

Cluster 처리 부담이 적다.

단점

FAT 영역이 커진다

버려지는 용량(Slack) 많다.

 

- 클러스터 크기에 따른 장단점이 있으나 클러스터 크기가 작은 경우 버려지는 용량이 적다. 반면  경우 버려지는 용량이  경우 버려지는 용량이 크기 때문에 NTFS 파일시스템에선 클러스터 할당 크기를 작게  놓았다.

- 영상 데이터 같이 대용량 파일만 저장하는 경우 굳이 클러스터 크기를 작게  필요는 없을 것이다.

 

 

2. FAT 파일시스템 구조

1) FAT 파일시스템의 구조

 

[FAT 16 파일시스템 구조]

 

[FAT 32 파일시스템 구조]

 

- FAT16 FAT32 구조적인 부분에서 차이점은 FAT16 경우 루트 디렉토리 영역이 따로 존재한다.

- FAT16 경우 루트 디렉토리 영역이 따로 존재하며 하위 디렉토리들과 달리 FAT#2 영역 뒤에 오지만 FAT32 경우 일반 디렉토리 중의 하나로 간주 하기 때문에 데이터 영역 어디서든지 상관 없다.

 

2) 부트 레코드(Boot Recort)

- 볼륨의  번째 섹터이며, 예약된 (Reserved) 영역의  번째 섹터이다.

- Windows 부팅을 시키기 위한 기계어 코드와 FAT 파일시스템의 여러 설정 값들이 있다.

- BIOS Parameter Block(BPB)이라고도 하며, 크기가 1섹터밖에  되는 작은 영역이지만 Windows FAT 파일 시스템을 인식하는데 가장 중요한 역할을 담당한다.

- 만약 볼륨 내용이 완벽히 살아있어도 부트레코드 섹터의 데이터가 지워지면 Windows에서는 해당 볼륨을 인식   없다.

 

3) 예약된(Reserved) 영역

- FAT16 경우 1섹터, FAT32 경우 32섹터를 할당한다.

- FAT16 경우 1섹터 바로 뒤에 FAT영역이 오며  영역은 미래를 위해 예약되어 있는 영역으로 사용하지 않는 영역이다.

- 만약 특별한 정보를 기록 하고 싶은 경우  영역에 기록 하면  것이다.  FAT32 경우 예약된 영역 안에  개의 섹터를 사용하여 저장하고 있으므로 겹치지 않게 유의해야 한다.

 

 

4) FAT #1 영역과 FAT #2 영역

- 클러스터들을 관리하는 테이블이 모여 있는 공간으로 FAT 영역을 통해 어떤 클러스터가 비어 있는지, 어떤 파일에 어떤 클러스터가 연결되어 있는지   있다.

-  영역에 손상이 된다면 상당히 위험하며, 백업본을   이상 만들어 두게 해놓았다.

- FAT #1영역과 #2영역은 동일한 내용을 담고 있다. FAT #2영역은 일종의 백업본으로 FAT #1 손상이 갔을 경우를 대비해 만들어 져있다.

- FAT 영역은 최소 1 이상 되어야 한다. 보통 2개의 영역을 만든다.

 

5) 루트 디렉토리 영역(FAT 16 존재)

- FAT 16에서는 루트 디렉토리의 위치가 무조건 FAT #2 영역 뒤쪽으로 고정되어 있다.(크기는 가변적일  있다.)

-  방식은 루트 디렉토리가 어디에 있는지를 조사할 필요가 없어서 개발의 편의성이 증가하게 되는 장점이 있지만, 루트 디렉토리의 파일 개수 제한  단점이 있어서 FAT32에서는 구조가 바뀌었다.

- FAT32에서는 루트 디렉토리가 데이터 영역 어디에 와도 상관이 없으며, 단지  위치를 부트 레코드에 기록하고 있을 뿐이다.  방법의 유일한 단점은 부트 레코드가 망가지면 루트 디렉토리를 찾아내기가 매우 힘들다.

- 따라서 대부분의 FAT 코드들이 FAT32 경우라도 FAT #2 영역 바로 뒤에 루트 디렉토리를 위치시킨다. 따라서 부트 레코드가 망가져서 루트 디렉토리의 위치를   없게 되어버린 경우에도 간단히 찾을  있다.

 

 

6) 데이터 영역(파일과 디렉토리)

-  영역에는 파일 또는 디렉토리가 저장되어 있으며, 클러스터(Cluster)라고 불리는 논리적인 단위로 읽기/쓰기가 된다.

 

 

7) 사용하지 않는(Unused) 영역

-  영역은 물리적으로 사용해도 상관없지만, FAT 파일시스템이 볼륨이 구조화시키는 과정에서 잉여분이 조금 남는 영역이다.

- 매우 작은 양이기 때문에 무시할  있을 정도이다.

 

8) 영역별 정리

구분

부트 레코드

예약된 영역

FAT 영역

데이터 영역

크기

1섹터

1~32섹터

파티션의 1~0.5%

파티션의 99% 이상

접근 단위

섹터

섹터

섹터

클러스터

 

 

 

3. 부트레코드

1) 부트레코드 소개

- BIOS Parameter Block(BPB)라고 불리는  영역은 해당 볼륨의 여러 가지 설정 값들을 저장하고 있으며, 해당 볼륨이 부팅될  있도록 부팅에 필요한 실행 코드도 포함하고 있다.

- Boot Record 저장되는 위치는 해당 볼륨의  번째 섹터이며 FAT 파일 시스템을 구성하는 영역   앞에 위치하는 예약된(Reserved) 영역의  번째 섹터이기도 하다.

- HDD 볼륨이 여러  있다면 각각의 볼륨에 부트 레코드도 하나씩 있게 된다.

- 하드디스크에 파티셔닝이 되어 있지 않아 볼륨이 1개인 경우 부트레코드는 MBR(Master Boot Record) 영역에 존재한다.

 

[파티션 없이 단일 볼륨인 하드디스크의 부트 레코드 위치]

 

[파티션이 존재하고 볼륨이 2개인 하드디스크의 부트 레코드 위치]

 

 

2) 부트레코드에서 FAT 16 FAT 32 차이

[FAT16 FAT32 부트 레코드 레이아웃]

 

- 32오프셋 까지는 공통된 항목을 가졌으나 32 이후에는 항목이 달라 서로호환이 않된다.

- FAT16 부트 레코드 구조체와 FAT32 부트 레코드 구조체를 따로 선언해야 되는 것이다.

- 볼륨의 파일시스템을 조사할  0~32 오프셋까지 공통 항목의 내용을 조사하면   있을 것이다.

 

3) FAT 16 부트 레코드

 

[FAT16 부트레코드 영역]

 

- 512Byte 16진수 내용이 FAT16 부트 레코드의 모든 것이다.

- Windows FAT 파티션을 인식하는데 필수적인 사항들이기 때문에  들어가야 한다.

- 색칠이 되어있지 않은 부분은 부팅할  사용하는 부트 코드 영역이다.  파티션을 부팅용으로 사용하지 않을 경우 모두 '0'으로 채워도 상관 없는 영역이다.

 

[FAT16 부트 레코드 항목]

 

- FAT 파일시스템은 리틀 엔디언(Little Endian)으로 설계되어 있기 때문에 출력되는 값들은 실제 저장한 값과 반대로 뒤집혀 있다.

 

 

4) FAT 16 FAT 32 부트 레코드 공통 항목 설명

 

이름

Jmp Boot Code

위치

(Offset)

0~2

크기

(Size)

3 Byte

일반적인 

(Value)

EB 3C 90

설명

부트 코드로 점프하는 명령어. x86 계열 PC 부팅   항목을 읽어서 부트 코드가 있는 영역으로 점프하게 된다. 해당 볼륨을 이용해서 부팅할 일이 없다면 어떤 값이 와도 무방하다.

 

이름

OEM Name

위치

(Offset)

3~10

크기

(Size)

8 Byte

일반적인 

(Value)

ASCII Code

'MSDOS5.0'

설명

 항목은 단지 OEM 회사를 나타내기 위한 문자열일 뿐이다. Windows  항목에 대해서 어떠한 참조도 하지 않는다. Windows 보통 'MSWIN4.1'이라고 적거나 'MSDOS5.0'이라고 적는다.  항목은 어떤 값이 되어도 상관없다.

 

이름

Byte Per Sector

위치

(Offset)

11~12

크기

(Size)

2 Byte

일반적인 

(Value)

512

설명

섹터당 바이트 수를 적는다. 하드 디스크의  섹터가  바이트를 담고 있는지에 대한 항목으로  항목은 필수적으로 다음 값만 적어주어야 한다.

512, 1024, 2048, 4096 (  외에 다른 값은 사용하지 않는다.)

Windows 다른 FAT 인식하는 장치들에서 여러분이 기록한 FAT 하드디스크가  인식되길 바란다면  항목에 512 되어야  것이다.

섹터당 크기가 512Byte 아닌 하드디스크는 거의 없기 때문에 512 적는 것이 좋다.

 

이름

Sector Per Cluster

위치

(Offset)

13

크기

(Size)

1 Byte

일반적인 

(Value)

32

설명

클러스터당 섹터 ,  클러스터가  개의 섹터로 이루어져있는지 기록되는 부분이다.

 값은 반드시 '0'보다  값이어야 하며 2 배수여야 한다.

 

이름

Reserved Sector Count

위치

(Offset)

14~15

크기

(Size)

2 Byte

일반적인 

(Value)

FAT16 '1'

FAT 32 '32'

설명

예약된(Reserved) 영역의 섹터 수를 저장한다. 예약된 영역의 크기는 최소한 1 섹터 이상 되어야 하는데, 그것은 부트 레코드가 예약된 영역의  번째 섹터에 위치하기 때문이다.

따라서  항목의 값이 '0' 되어서는  된다.

FAT16 경우 보통 '1' 값을 가지며, FAT 32 보통 '32'값을 가진다.

예약된 영역에서 부트 레코드나 특별히 다른 정보를 담지 않고 있는 섹터는 전부 '0'으로 초기화 되어있어야 한다.

 

이름

Number of FATs

위치

(Offset)

16

크기

(Size)

1 Byte

일반적인 

(Value)

2

설명

볼륨에 있는 FAT 영역의 수를 담고 있다.  항목은 최소한 '1' 이상의 값을 가지며, '2'라고 적는 것을 추천한다.

주의할 점은  항목에다 '2'라고 적어놓고 실제 FAT영역을 3 만들거나 1 만드는 것은 좋지 않다.

CF Card 같은 저용량 매체에서 data 영역을 조금이라도  확보하기 위해  항목을 '1' 지정하는 개발자도 있다.

Windows  항목의 값이 '1'이더라도 호환성을 보장하지만, 모든 FAT코드가 호환할  있을지는 장담   없다. Number of FATs 값이 무조건 '2' 것이라는 가정하에 코드를 짜는 개발자들도 있을  있다.

 

이름

Root Directory Entry Count

위치

(Offset)

17~18

크기

(Size)

2 Byte

일반적인 

(Value)

FAT16 '512'

FAT 32 '0'

설명

FAT16 경우  항목은 루트 디렉토리에  개의 엔트리(파일과 디렉토리 모두 1개의 엔트리로 친다.) 수용할 것인지 나타낸다.  항목의 값은 Bytes Per Sector 짝수이어야 한다.  Bytes Per Sector 512라면  항목은 512, 1024,2048 같이 들어갈  있다. 최대 호환성을 위해서는 '512' 적는게 좋다.

주의할 점은 FAT32에서는 반드시 '0'이어야 한다.

 

이름

Total Sector 16

위치

(Offset)

19~20

크기

(Size)

2 Byte

일반적인 

(Value)

FAT16 가변적

FAT 32 반드시 '0'

설명

 항목은 볼륨상에 있는  섹터 수를 2Byte 나타낸다.

저장장치의  섹터수가 아닌 볼륨의  섹터 수이며 볼륨에 존재하는 모든 영역을 합친 섹터 수를 기록해야 한다.  항목의 값이 '0'   있다. 만약  항목의 값이 '0' 이라면 아래 설명할 Total Sector 32 항목의 값은 절대 '0' 되어서는  된다. FAT 16 경우  섹터수가 '0xFFFF'보다 크다면 Total Sector 32 항목을 이용한다.

FAT32에서는  항목의 값이 반드시 '0'이어야 한다.

 

이름

Media

위치

(Offset)

21

크기

(Size)

1 Byte

일반적인 

(Value)

0xF8

설명

 항목은  볼륨이 어떤 미디어에 저장되어 있는지를 나타낸다. 보통은 고정식 디스크 값인 '0xF8' 쓰인다. CF Card USB Memory 이동식이므로 '0xF8' 아닐 것이라고 생각 하기 쉽다. 실제 Windows CF Card 포맷하면  항목 값을 '0xF8'이라고 적는다.

플로피 외의 저장장치라면  항목에 '0xF8' 적는 것이 좋다.

 

[Media 항목에 들어갈  있는 값들]

용량

저장장치 형태

0xF0

2.88MB

3.5inch, 양면, 36 sector 플로피디스크

0xF0

1.44MB

3.5inch, 양면, 18 sector 플로피디스크

0xF9

720KB

3.5inch, 양면, 9 sector 플로피디스크

0xF9

1.2MB

5.25inch, 양면, 15 sector 플로피디스크

0xFD

360KB

5.25inch, 양면, 9 sector 플로피디스크

0xFF

320KB

5.25inch, 양면, 8 sector 플로피디스크

0xFC

180KB

5.25inch, 단면, 9 sector 플로피디스크

0xFE

160KB

5.25inch, 단면, 8 sector 플로피디스크

0xF8

X

고정식 디스크(플로피를 제외한 모든 장치

 

이름

FAT size 16

위치

(Offset)

22~23

크기

(Size)

2 Byte

일반적인 

(Value)

FAT16 가변적

FAT 32 반드시 '0'

설명

 항목은 FAT 영역의 섹터 수를 저장하는 항목이다. FAT #1영역과 FAT #2영역을 합친 값이 1개의 FAT 영역의 섹터 수를 적어야 한다.  항목은 FAT12 FAT16 사용한다.

FAT32 FAT Size 32라는 항목이 따로 있다. 중요한 점은 FAT32에서는 항목의 값이 반드시 '0'이어야 한다.

 

이름

Sector Per Track

위치

(Offset)

24~25

크기

(Size)

2 Byte

일반적인 

(Value)

63

설명

 항목은 x86 프로세서 계열에서 발생하는 인터럽트(INT) 0x13 위해 존재하는 항목으로 저장장치의 트랙당 섹터 수를 저장한다.

Windows 계열에서는  이상  항목을 참조 하지 않는다.

 

이름

Number Of Heads

위치

(Offset)

26~27

크기

(Size)

2 Byte

일반적인 

(Value)

255

설명

 항목은 x86 프로세서 계열에서 발생하는 인터럽트(INT) 0x13 위해 존재하는 항목으로 저장장치의 헤더 수를 저장한다. Windows 계열에서는  이상  항목을 참조 하지 않는다.

 

 

이름

Hidden Sector

위치

(Offset)

28~31

크기

(Size)

4 Byte

일반적인 

(Value)

32

설명

 항목은 x86 프로세서 계열에서 발생하는 인터럽트(INT) 0x13 위해 존재하는 항목으로 해당 봉륨 앞에 존재하는 (숨겨진) 섹터 수를 저장한다. 파티션되지 않은 볼륨에서는 반드시 '0'이어야 한다. Windows 계열에서는  이상  항목을 참조 하지 않는다.

 

 

이름

Total Sector 32

위치

(Offset)

32~35

크기

(Size)

1 Byte

일반적인 

(Value)

32

설명

 항목은 볼륨상에 있는 섹터 수를 4Byte 나타낸다. 중요한 것은 저장장치의  섹터 수가 아닌 볼륨의  섹터  라는 점이다. 볼륨에 존재하는 모든 영역을 전부 합친 섹터 수를 적어야 한다. FAT16 경우  항목의 값이 '0'   있다.

 항목이 '0'이라면 위에서 설명한 Total Sector 16 항목의 값은 절대 '0' 되어서는  된다. FAT32에서는  항목의 값이 반드시 '0' 아니여야 한다.

FAT32 Total Sector16 항목은 이용하지 않고, Total Sector32 만을 이용해야 한다.

 

여기까지는 동일한 항목이다.

여기서 클러스터의 크기를 알아보도록 하자.

Sector Per Cluster X Bytes Per Sector = 클러스터 크기

 

Sector Per Cluster : 02 (2)

Byte Per Sector : 00 02(Little Endian) => 0200(16진수)=> 512

2 X 512 = 1024 Byte,  1KB 되는 것이다.

 

 

5) FAT 16 부트 레코드 항목 설명

 

이름

Drive Number

위치

(Offset)

36

크기

(Size)

1 Byte

일반적인 

(Value)

0x80

설명

 항목은 x86 프로세서 계열에서 발생하는 인터럽트(INT) 0x13 위해 존재하는 항목으로 INT 0x13에서 정의한 드라이브 값을 설정한다.

플로피 디스크는 '0x0',  외의 대부분의 저장장치는 '0x80' 값을 가진다.

Windows계열에서는  이상  항목을 참조하지 않는다.

 항목은 FAT 16에만 해당한다.

 

이름

Reserved1

위치

(Offset)

37

크기

(Size)

1 Byte

일반적인 

(Value)

0

설명

Windows NT 계열에서 사용하려고 만든 항목으로  값은 항상 '0'으로 해야 한다.

 항목은 FAT 16에만 해당 한다.

 

이름

Boot Signature

위치

(Offset)

38

크기

(Size)

1 Byte

일반적인 

(Value)

0x29

설명

확장 부트 서명,  항목의 값은 항상 '0x29'여야 한다.  항목이 의미하는 것은 여기가 끝이 아니라  항목 아래쪽에 3가지 항목이  추가되었다는 것을 말한다.

 항목은 FAT16에만 해당 한다.

 

이름

Volume ID

위치

(Offset)

39~42

크기

(Size)

4 Byte

일반적인 

(Value)

가변적임

설명

볼륨 시리얼 번호, 고유의 임의의 시리얼 번호를 생성해서 기록한다.

 항목은 FAT16에만 해당 한다.

 

이름

Volume Label

위치

(Offset)

43~53

크기

(Size)

11 Byte

일반적인 

(Value)

가변적임

설명

해당 볼륨의 레이블을 적는다. 여기에 적은 문자열은 Windows에서 드라이브 이름으로 쓰이게 된다. 사실 볼륨 레이블을 적는 부분은 2군데가 있는데,   한곳은 여기이고, 다른  곳은 루트 디렉토리에 존재하는 레이블 엔트리이다. 볼륨 레이블이 변경될 경우 2군데 모두 값을 바꿔 주어야 한다. 루트 디렉토리에 레이블 엔트리를 생성하지 않았다면  항목은 "NO NAME "라고 적어야 한다.

 항목은 FAT16에만 해당 한다.

 

이름

File System Type

위치

(Offset)

54~61

크기

(Size)

8 Byte

일반적인 

(Value)

ASCII Code

"FAT16 "

설명

 항목에는 "FAT12 ", "FAT16 ", "FAT "  적절한 문자열을 적어주면 된다. 주의  점은 여러분이 어떤 하드디스크가 FAT16인지 FAT32인지를 조사할   영역에 적힌 값을 이용해서 판단을 내리면  된다는 점이다.  항목은 단순한 문자열이며  외에 어떤 의미도 없기 때문에  영역을 잃고 볼륨을 FAT16이라고 결론을 내리면  된다.

 항목은 FAT16에만 해당 한다.

 

 

6) FAT 32 부트 레코드 항목 설명

 

[FAT32 부트 레코드 항목]

 

 

이름

FAT Size 32

위치

(Offset)

36~39

크기

(Size)

4 Byte

일반적인 

(Value)

가변적임

설명

 항목은 Fat 영역의 섹터 수를 저장하는 항목이다. FAT #1영역과 FAT #2영역을 합친 값이 아닌 1개의 FAT 영역의 섹터 수를 적어야 한다.

 항목은 FAT 32에만 해당한다.

 

이름

Ext Flags

위치

(Offset)

40~41

크기

(Size)

2 Byte

일반적인 

(Value)

0x00

설명

FAT 테이블을 조작하는 것에 대하여 여러 설정 값을 가진다.

 항목은 FAT 32에만 해당한다.

 

[Ext Flags]

 

속성 이름

크기

설명

Active FAT Number

4Bit

활동시킬 Fat 번호, 0부터 시작한다. Flag 값이 '1' 경우에만 의미가 있다.

Reserved

3Bit

사용되지 않음. 미래를 위해 예약된 공간.

Flag

1Bit

0 : 변경 내용을 모든 FAT 영역에 반영하라.

1 : 변경 내용을 Active FAT Number 적힌 FAT 영역에만 반영하고 다른 영역에는 손대지 마라.

Reserved

8Bit

사용되지 않음. 예약된 공간.

 

 

이름

File System Version

위치

(Offset)

42~43

크기

(Size)

2 Byte

일반적인 

(Value)

0x00

설명

 항목은 FAT32 버전 정보를 나타낸다. 상위 바이트는  버전, 하위 바이트는  버전을 나타낸다. Windows  항목을 조사하여 만약 적혀 있는 버전이 자신이 인식할  있는 버전보다 높다면 파티션 인식 작업을 포기하게 된다. 지금까지 나온 FAT 버전은 전부 0x00으로 설정되어 있고, 아직까지  버전 정보가 올라간 적이 없다. 따라서 0x00으로 설정하는 것이 좋다.

 항목은 FAT 32에만 해당한다.

 

이름

Root Directory Cluster

위치

(Offset)

44~47

크기

(Size)

4 Byte

일반적인 

(Value)

2

설명

 항목은 루트 디렉토리의 클러스터 값을 담고 있다. FAT16 다르게 FAT32 경우 루트 디렉토리가 데이터 영역 어디에 와도 상관 없기 때문에 루트 디렉토리의 시작 위치를 적어  것이다. 보통의 경우  항목의 값은 2 들어가는데 여러분들도  값을 따르는  좋다. 만약 실수로  항목의 값이 엉망이 되어 버렸다면 루트 디렉토리의 위치를   없게 되는데 항상 루트 디렉토리를 클러스터 2 위치시켜 놓는다면 쉽게 루트 디렉토리의 위치를 복구할  있다.

 항목은 FAT 32에만 해당한다.

 

이름

File System Information

위치

(Offset)

48~49

크기

(Size)

2 Byte

일반적인 

(Value)

1

설명

 항목은 FSInfo 구조체가 어디에 저장되어 있는지를 가리킨다. 보통의 경우 볼륨의 1 섹터에 저장된다.

 항목은 FAT 32에만 해당한다.

 

이름

Boot Record Backup Sector

위치

(Offset)

50~51

크기

(Size)

2 Byte

일반적인 

(Value)

6

설명

부트 레코드는 매우 중요하기 때문에 백업을 해두는데  위치를 기록 한다. 사본의 위치는 예약된(Reserved) 영역 어디에 오든 상관없지만, 일반적으로 6 섹터를 이용한다.  항목의 값이 '0'이라면 백업을 하지 않았다는 의미이다.

 항목은 FAT 32에만 해당한다.

 

이름

Reserved

위치

(Offset)

52~63

크기

(Size)

12 Byte

일반적인 

(Value)

0

설명

미래를 위해 예약된 영역이다. 항상 0으로 채워져 있다.

 항목은 FAT 32에만 해당한다.

 

이름

Drive Number

위치

(Offset)

64

크기

(Size)

1 Byte

일반적인 

(Value)

1

설명

 항목은 인터럽트(INT) 0x13 위해 존재하는 항목이다. Windows 계열에서는  이상  항목을 참조하지 않으므로 어떤 값을 써도 무방하다.

 항목은 FAT 32에만 해당한다.

 

이름

Reserved1

위치

(Offset)

65

크기

(Size)

1 Byte

일반적인 

(Value)

0

설명

Windows NT 계열에서 사용하려고 만든 항목이다.  항목은 항상 0으로 해야 한다.

 항목은 FAT 32에만 해당한다.

 

이름

Boot Signature

위치

(Offset)

66

크기

(Size)

1 Byte

일반적인 

(Value)

0x29

설명

확장 부트 서명이다.  항목의 값은 항상 '0x29'여야 한다.  항목이 의미하는 것은 여기가 끝이 아니라  항목 아래쪽에  가지 항목이  추가되었다는 것을 말한다.

 항목은 FAT 32에만 해당한다.

 

이름

Volume ID

위치

(Offset)

67~70

크기

(Size)

4 Byte

일반적인 

(Value)

가변적임

설명

볼륨 시리얼 번호이다. 고유의 임의의 시리얼 번호를 생성해서 기록한다.

 항목은 FAT 32에만 해당한다.

 

이름

Volume Label

위치

(Offset)

71~81

크기

(Size)

11 Byte

일반적인 

(Value)

가변적임

설명

볼륨 레이블로 해당 파티션의 볼륨 레이블을 적는다. 여기에 적은 글자는 Windows에서 디스크 이름으로 쓰인다. 볼륨 레이블을 적는 부분이 2군데가 있는데   하나는 여기이고, 다른  곳은 루트 디렉토리이다. 볼륨 레이블이 변경될 경우 2군데 모두 값을 바꿔야 한다. 볼륨 레이블이 없는 경우 "NO NAME "이라고 적어준다.

 항목은 FAT 32에만 해당한다.

 

이름

File System Type

위치

(Offset)

82~89

크기

(Size)

8 Byte

일반적인 

(Value)

ASCII Code

"FAT32 "

설명

Windows  항목에 항상 "Fat32 "라고 적는다.  항목은 단순한 문자열이며  외에 어떤 의미도 없기 때문에  영역을 잃고 볼륨을 FAT32이라고 결론을 내리면  된다

 항목은 FAT 32에만 해당한다.

 

- 부트 레코드 항목 중에는 문자열을 저장하는 항목이 있는데 OEM Name, Volume Label, File System Type 항목은 문자열을 저장한다.

-  항목에 문자열을 저장하는  있어서 주의해야  점은  공간을 반드시 Space(0x20) 처리해야 한다.

만약 C언어로 File System Type "FAT16"이라는 문자를 넣을  코드를 보자

 

strcpy(FileSystemType, "FAT16");

이렇게 되면 저장되는 모양은

F

A

T

1

6

NULL

X

X

0x46

0x41

0x54

0x31

0x36

0x0

  없음

  없음

 

하지만  공간에는 Space 들어가야 한다.

아래 형태로 구현을 하기 위해 문자열 제어 함수를 사용하지 말고 직접 특별한 문자열 제어 함수를 구현하던지 배열 값을 넣어야 한다.

F

A

T

1

6

Space

Space

Space

0x46

0x41

0x54

0x31

0x36

0x20

0x20

0x20

 

 

7) 부트 레코드의 끝부분

- 부트 레코드를 구현하는데 있어 FAT16/32 모두 부트 레코드 영역의 Offset 511~512에는 반드시 0xAA55라는 서명이 들어간다.

 

이름

Signature

위치

(Offset)

510~511

크기

(Size)

2 Byte

일반적인 

(Value)

반드시 0xAA55

설명

 항목은 부트 레코드가 손상되었는지 아닌지 확인 하는 용도로 사용하며  항목이 없다면 Windows에서는 부트레코드가 망가졌다고 판단하고 인식을 못하게 된다.

 항목은 FAT 16, FAT32에만 모두 해당한다.

 

많은 FAT문서에는 "Signature 위치는 부트 레코드 가장 끝부분" 이라고 하지만 이것은 섹터가 512Byte 경우에만 유효한 내용이며, Microsoft에서 제공하는 공식 문서에는 섹터의 크기가 512Byte보다  경우라도 Signature Offset 510~511 와야한다.

 

 

4. 부트레코드 실습

 

관리자권한으로 실행

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

 

#include <stdio.h>

#include <windows.h>

#include <stdlib.h>

 

#define U8        unsigned char

#define S8        char

#define U16        unsigned short

#define U32        unsigned int

#define U64        unsigned __int64

 

#pragma pack(1) //컴파일러에게 구조체 패딩을 넣지 말라고 선언

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;

#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;

 

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

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

 

VolStruct gVol;

 

int main(void){

    U8            buf[512];

 

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

    gVol.VolBeginSec = 0x87; //부트 섹터가 있는 곳

 

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

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

        return 1;

    }

 

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

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

        return 1;

    }

 

    printf("[[[[[[[[[Volume Information ]]]]]]]]]\n");

    printf("Total Sector            = %d Sectors : %dMB\n",gVol.TotalSec, ((gVol.TotalSec*512)/1024)/1024);

    printf("FAT Size            = %d Sectors \n",gVol.FATSize);

    printf("Root Dir Sector            = %d      \n", gVol.RootDirSec);

    printf("Root Dir Sector Count        = %d \n", gVol.RootDirSecCnt);

    printf("First Data Sector        = %d \n",gVol.FirstDataSec);

    printf("Data Sector Count        = %d Sectors \n",gVol.DataSecSize);

    printf("Total Cluster            = %d            \n", gVol.TotalClusCnt);

    printf("Size of Cluster            = %d : %dKB        \n",gVol.ClusterSize, gVol.ClusterSize/1024);

 

    return 0;

}

 

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;

 

}

 

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;

}

[드라이브 설정]

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

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

 

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

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

 

 

[부트레코드 섹터위치]

 

gVol.VolBeginSec = 0x87;

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

 

실행화면..

 

 

1.파티션 소개

 

1) 파티션이란?

파티션은 연속된 저장 공간을 하나 이상의 연속되고 독립된 영역으로 나누어서 사용할  있도록 정의한 규약이다.

 

간단하게 파티션 사용한 예를 보도록 하자.

- 파티션의 경우 OS혹은 OS 제작한 벤더에서 만든 일종의 약속 같은 것이다.

- 컴퓨터 만드는 제조사와 관계도 있고, OS문제도 있기 때문에 어떤 PC 어떤 OS 사용될지에 따라 파티션과 파일시스템을 어떤걸 사용하게     없게 된다.

- 파티션을 나누기 위해서는 저장장치에 연속된 공간에 있어야 한다.  하나의 하드디스크에는 여러 개의 파티션을 나눌  있지만,  개의 하드디스크를 가지고 하나의 파티션을 만들  없는 것이다.

- 그림 B C 가장  차이점은 MBR 유무이다.  부분은 다음 챕터에서 진행  것이다.

 

 

2) 파티션 정보의 관리

 그림의 경우 PC 부트 시퀀스가 BIOS 거쳐 부팅 가능한 파티션으로 가기 위한 표지판 앞을 지나고 있다.

부트 시퀀스는 컴퓨터에 전원이 들어온 이후부터 메모리에 커널이 적재되기까지의 일련의 과정이다.

, 부팅 과정이라고   있다.

 

- 파티션을 나누어 사용하는 경우 MBR(디스크 0 섹터) 영역 내에 '파티션 테이블' 있다.

- 파티션 테이블에 파티션에 대한 여러가지 자세한 정보를   있다.

 

DOS 파티션 테이블 속성

DOS 파티션 테이블을 이용하여 알아낼  있는 정보

참고

파티션의 부팅 가능 여부

 

하드디스크 읽는 모드

CHS/LBA

파티션의 시작   위치

 

파티션 타입

파일시스템 혹은 사용 용도를 나타냄.

파티션에서 사용되는 섹터의 개수

이를 활용하여 파티션의 크기를   있다.

 

- 부트시퀀스가 BIOS 지나 하드디스크 MBR 영역  파티션 테이블의 위의 속성을 알고 있고,   해당 파티션으로 이동해서 안정적으로 부팅을 하거나 다른 파티션(확장 파티션)으로 이동 하게 된다.

 

3) 파티션의 사용 용도

. 하나의 물리적인 디스크를 여러 논리 영역으로 나누어 관리를 용이하게 한다.

. OS 영역과 Data 영역으로 나누어 OS 영역만 따로 포멧  관리를 하기 위해 사용한다.

. 여러 OS 설치 하기 위해 사용한다.

. 하드 디스크의 물리적인 배드 섹터로 특정 영역을 잘라서 사용하기 위해 사용한다.

- ~  경우 OS 문제시 데이터 영역 전체 손실을 막기 위함이고 라의 경우 하드디스크 물리적 배드 섹터가 생긴 경우  부분을 피해 다른 영역에 정상적으로 사용할  쓰는 방법이다. 하나의 팁이  것이다.

- 혹은 하드 디스크가 구형이라 파일시스템 FAT 16 읽을  있는 경우 효율적으로 활용하기 위해 파티션을 나누는 방법도 있다.

 

 

4) 파티션과 볼륨의 차이점

볼륨 : OS Application 등에서 이용할  있는 저장 공간,  섹터(Sector) 집합이다.

- 연속된 공간이 아니여도 볼륨으로   있다.  2개의 하드디스크를 사용하는 경우 하나의 하드디스크처럼 인식하여 사용할  있다.

 

[2개의 하드디스크를 1개의 볼륨으로 인식한 ]

- 파티션 역시 저장 공간이기 때문에 볼륨으로   있다. 하지만 반드시 연속된 섹터의 집합 이여야만 한다.

- 만약 하나의 파티션에 용량이 부족할  볼륨의 개념을 활용하면 2개의 파티션을 1개의 볼륨으로 사용할  있겠지만, 그렇지 않다면 파티션은 반드시 연속된 공간을 활용해야 하기 때문에 기존의 파티션 2개를 삭제한  새로운 파티션을 다시 설치 해야 한다.

 

[하나의 물리적 디스크를 여러 파티션으로 나눈 경우]

 

2.MBR

1)MBR이란?

Master Boot Record Boot Record 메인 격이다.

-   레코드는  파티션의  번째 섹터에 위치하며, 주로 해당 파티션의 설치된 OS 부팅 하는 역할을 하게 된다.  OS 실행을 위한 부트로더(Boot Loader) 호출하는 것이다. 파티션을 나누지 않은 경우라면 부트레코드는 MBR 있을 것이다.

- 단일 파티션을 사용하는 플로피의 경우 부트레코드는 1개만 있을 것이다. 따라서 MBR영역이 필요 없다.

 

[MBR 호출 과정]

- PC 경우 BIOS라는 펌웨어가 시스템 내의 장치들을 검사한  이상 없다면 저장장치의 가장 앞부분으로 점프할 것이다. 이때 단순히  번째 섹터로 이동하는 것이므로 MBR이든 BR이든 부팅 가능한 프로그램만 실행   있으면 될것이다.

- 다른 시스템의 경우 나름대로  번째 섹터를 사용하는 방법이 다르고 부르는 말도 다르다.

 

 

2)MBR 역할

- 디스크 0 섹터에 위치하며 부팅에 필요한 부트 코드와 파티션 테이블을 포함하고 있다.

- MBR 영역 내에 프로그램된 부트 코드의 경우 부팅이 목적이며 커널이 적재되기 전까지 하나하나의 과정들의 목표는 부팅 과정에 있어 다음 단계를 호출 하는 것이 된다.

- MBR 경우 BR 호출하는 과정을 위한 프로그램이  것이다. 따라서 파티션 테이블을 읽고, 각각의 파티션이 부팅 가능한지 확인하며, 파티션이 정상적이지 않거나 부팅 가능한 파티션이 없는 경우 예외 처리도 해야한다.

- 최종적으로 부팅 가능한 파티션의 실제 주소(Address) 계산하여 해당 파티션의 Boot Record 호출하는 것이 주요 목적이 될것이다.

- Microsoft 경우 MBR영역에 파티션 테이블까지 입력하게 되기 때문에 512Byte 작은 공간이 아닐 것이다.

 

 

3)BIOS 부트 시퀀스

- 부트 시퀀스(Boot Sequence) 시스템에 전원이 들어오거나 시스템을 재시작하는 경우 커너을 로드하기까지의 일련의 과정이라고   있다.  과정 중에는 주기억장치에서 커널을 메모리에 적재하는 등의 여러 가지 작업들이 있지만,  중에서도 가장 선행되는 작업은 BIOS(Basic Input/Output System) 의해 행해지는 검증 작업니다.

- BIOS PC에서 전원이 들어오자마자 구동되는 플래시 메모리(Flash Memory) 내의 펌웨어이며, 보통 ROM BIOS라고도 불리낟.

- BIOS CPU 상태를 비록, 메모리와 여러 장치, 포트 등이 사용가능한지 검증  자제 테스트를 진행하고, 커널과 PC 디바이스간의 인터페이스를 제공한다.  우리가 사용하는 OS 내지 응용프로그램에서 하드웨어를 호출할  있는 창구 역할을 하게 되는 것이다.

 

[BIOS 역할]

  1. 시스템에 장착된 모든 하드웨어들이 정상 동작하는지 테스트하는 POST(Power-On-Self-Test) 시행한다.  과정에서 메모리, 하드디스크, CD-ROM, 키보드 등의 문제가 있으면 비프(beep)음이나 화면에 오류를 출력한다.

  2. 동일 시스템에 다른 BIOS 있을 경우 이를 호출하여 활성화 시킨다. 그래픽 카드나 SCSI카드에 동립적인 BIOS 있는 경우 메인보드의 BIOS 이들을 활성화 시켜주어야 한다.

  3. 하드디스크나 보드의 클럭등을 관리 기능을 제공한다. CMOS 셋업을 통해 메인보드를 비롯한 주변장치들의 설정을 변경할  있으며, 여기서 설정한 데이터는 메인보드 내의 EEPROM 저장된다.

  4. POST과정을 정상적으로 마친 경우 시스템에 연결되어 있는 주변장치들을 초기화 하며, 이는 차후 OS  장치들의 인터페이스를 제공할  있게 한다.

  5. 디스크의  번째 섹터를 읽어 들이며, DOS 경우 MBR 부트 코드를 실행시켜 소프트웨어적 부팅을 한다.

 

- 화면  콘솔창으로 출력하도록 별도 작업을 하지 않아도 모니터 화면을 통해 결과가 나오는 이유는 표준 출력(Standard Output) 모니터로 되어 있기 때문이다. 이는 OS BIOS 기본 출력을 모니터로 설정하였기 때문이다.  표준 출력을 다른 곳으로 바꿔준다면 파일이든 기타 출력장치(LED, Printer ) 부가 장치로 손쉽게 출력이 가능하다.

 

[BIOS 일반적인 시퀀스]

  1. PC 전원이 들어온  일련의 과정을 거친  BIOS 루틴이 시작됨

  2. 기존에 저장된 CMOS 설정 값을 읽는다. 여기서 읽는 값들은 하드웨어를 직접 제어할  있는 값으로 CPU 클럭 설정부터 주기억장치의 부트 시퀀스까지 많은 데이터들을 가져 온다.

  3. 인터럽트 핸들러와 장치 드라이버 로드한다. 인터럽트 핸들러는 비교적 초기에 설정되며, 인터럽트 벡터 역시 초기화된다.  인터럽트 벡터를 기반으로 대부분의 커널에서 확장된 입터럽트 벡터를 체적으로 생성하여 가지고 있으며, 이를 이용하여 응용프로그램 내외적 통신을 가능케 한다.

  4. 레지스터 영역과 파워 관리 영역을 초기화한다.

  5. POST 수행한다. 문제가 발견되면 그에 해당하는 비프음을 발생   정지한다. 정상이라면 BIOS  INT 19 발생시켜 다음 과정을 진행한다.

  6. 시스템 내의 비디오 카드를 검색하고, 비디오 카드 내의 BIOS 있는지 확인하여 있으면 이를 호출하여 비디오 카드 자체적 초기화를 진행하는데 대부분의 비디오카드는  시점에 비디오 카드의 간략한 정보를 화면에 출력하기도 한다.

  7. 기타 장치를 검색하고, 해당 장치내에 BIOS 있으면 이를 호출, 실행 시킨다. 이때 하드디스크 등을 검색한다.

  8. BIOS 시작 화면이 출력되며 기타 정보들을 화면에 디스플레이한다. BIOS 제조사  버전 정보, 제조일, Setup(F1, Del ), 시스템 로고, 시리얼 번호 등을 확인할  있다. PC 켜면 본격적인 부팅과정을   있는 화면이 나오는 것이다.

  9. 기타 장치들을 추가 테스트 진행한다.  장치들의 제조사에서 자체적으로 체크하는 루틴으로 에러 메시지가 화면에 출력되어 디버깅하기 용이 하며, 이때 메모리, 디스크 드라이브, DMA, 키보드  다양한 테스트를 진행한다.

  10. BIOS에서 검사한 시스템 정보들을 화면에 출력한다. 부트시퀀스  POST 과정이 완료 되는 것이다.

  11. OS 불러오기 위한 작업으로 부팅 가능한 저장장치를 검사하며, COMS 설정되어 있는 부팅 가능 순서대로 검사를 진행한다. 정상적인 저장장치가 검색되면 해당 장치의 부트 섹터를 호출한다.

 

- BIOS PC 시작부터 끝까지 모든 부분에 관여하며, 메인보드와 주변장치 사이의 인터페이스를 제공하고 초기화 하며 이를 이용하여 OS에서 디바이스들을 제어할  있게 한다.

 

4)MBR 구조

 

[Microsoft MBR 구조]

- MBR 부트코드와 파티션 테이블 영역으로 나눌  있다.

- 다른 OS 경우 대부분 부트 코드 영역이  섹터에 걸쳐 저장되거나 파티션 영역을 저장하는 영역도  섹터 또는  섹터에 걸쳐 저장하기도 하지만 Microsoft MBR에는 부트 코드와 파티션 테이블이   들어가 있는 것이 특징이다.

 

 

 그림처럼 0 섹터에서 512byte 부트 코드 영역을 상위 446Byte(블록지정한 부분) 사용 하고 있으며  뒤의 64Byte 파티션 테이블, 마지막의 2byte MBR 시그니처(Signature) 0xAA55 들어간다. IBM 호환 CPU특성상 리틀 엔디언(Little Endial) 의해 덤프를 하면 55 AA 나오는 것을   있다.

 

3.파티션 종류

1) DOS 파티션 테이블

- 가장 많이 알려진 파티션으로 Microsoft에서 출시된 MS-DOS 비롯한 Windows 제품군이 Dos 파티션을 사용하고 있으며 Linux 사용하고 있다.

- DOS 파티션 테이블이 물리적으로 위치하는 곳은 디스크 0 섹터  MBR 영역의 부트코드 바로 다음에 해당된다.

- 446번지로부터 64 Byte 경간을 파티션 테이블이 사용한다.

- 파티션 테이블은  4개의 파티션을 기록할  있으며 파티션 1개의 정보는 16Byte 표현한다.

- 파티션 테이블에 기록할  있는 파티션의 개수 제한극복을 위해 확장파티션을 지원하며 이를 이용하면 논리적으로 생성할  있는 파티션 개수의 제한이 없어지지만 결과적으로 DOS 파티션을 복잡하게 만드는 원인이기도 하다.

 

[DOS 파티션

 

 

 그림처럼 16Byte 마다 1나의 파티션에 대한 정보가 들어가게 된다  4개의 파티션까지 기록 가능 하다.

 

 

2) Apple Partition Map

- Apple 파티션 정보는 다른 벤더의 기록 방법과 약간 차이가 있다. Apple에서 사용하는 OS 기원인 Unix 점을 생각 해보면 Apple 파티션은 BSD 계열의 파티션 기록 방법을 보안한 느낌으로 애플에는 Apple Partition Map 라는 파티션이 존재하며  디스크에서 사용하는 파티션 정보를 기록 하고 있다.

- 파티션을 위한 파티션을 고려하여 제작 하였기 때문에 파티션 최대 개수는 Apple Partition Map 파티션의 크기와 관계가 있다.

- Apple Partition Map 파티션은 1 섹터에 있으며 0 섹터는 사용하지 않는다.

- 부트코드의 경우 Apple 컴퓨터의 경우 디스크에 기록하지 않고 PC 치면 BIOS레벨의 펌웨어에 기록하고 있으며  섹터의 512Byte 걸쳐 하나의 Partition Map Entry 기록 되며,  영역에 파티션의 위치  크기 정보 등이 함께 기록 된다.

- 파티션 영역의 크기는 '파티션 개수 X 512Byte' 된다.

- 그러나 하나의 Partition Map Entry 경우 136Byte 지나지 않아 낭비되는 것처럼 느껴질 수도 있으나, 여러 파티션들의 단점을 보안한 부분이기도 하다.

[Apple Partition Map]

 

 

3) BSD 디스크 레이블(BSD Disk Label)

- Unix 계열의 대표적인 BSD 시스템의 파티션으로 BSD 파티션은 하나의 섹터 안에 파티션 정보를 포함한 디스크 정보를 모두 담아 놓는다.

- DOS 파티션에 비해 매우 간단한 구조를 가지며, Apple Partition Map 보다 생성할  있는 파티션 개수에 제한이 있으나,  16개의 파티션을 가질  있다.

- 실제 저장되는 위치는 1 섹터이며, 148~403번지까지 256Byte 사용하며 하나의 파티션은 16Byte 표현 가능하다.

- 섹터 0번에는 부트 코드가 저장되어 있으며, 부트 코드 영역이 512Byte보다 크면 0 섹터에 이어서 2 섹터부터 16 섹터까지 추가로 기록할  있다.  이후부터는 파일시스템이 기록되므로 16 섹터를 벗어나선 않된다.

- FreeBSD, OpenBSD, NetBSD등이 있으며, 같은 뿌리를 가지고 있으나 사용방법부터 다른점이 많이 있다.

 

[BSD Disk Label]

 

 

 

4) 솔라리스 디스크 레이블(Solaris Disk Label)

- Sun사의 Solaris Disk Label BSD Disk Label 크게 다르지 않은 구조이다.

- BSD 마찬가지로 파티션 정보를 포함한 디스크 정보를 Disk Label 기록하며 512Byte 걸쳐 데이터를 저장한다.

- 선스팍과 PC   설치 가능 하며, 플랫폼에 따라 Disk Label 위치  데이터 구조가 달라진다.

 

 

Sun Sparc

- 0 섹터에 Disk Label 위치, 부트 코드는 1 섹터부터 15 섹터까지 위치

- 파티션의 위치  크기 정보를 나타내는 부분과 기타 추가적인 정보들을 저장하고 있는 영역으로 나뉘는데, 위치와 크기 정보는 Disk Label 444번지부터 64Byte 걸쳐 8개의 파티션을 기록할  있다.

- 파티션의 타입, 상태정보, Time stamp 등의 추가정보는 VTOC(Volume Table of Contents)라는 데이터 영역에 기록된다.

- VTOC Disk Label 128번지부터 134Byte 사용하며,  영역에는 파티션 정보뿐만 아니라 Disk 볼륨명, 사용하는 파티션 개수, 부팅 정보등이 추가로 기록 된다.

 

[ Sun Sparc Disk Label ]

 

i386 Solaris

- 반드시 DOS 파티션이 설치 되어야 한다.

- Solaris 부팅 파티션에는 파일시스템은 없어도 상관없지만, 부트코드를 가지고 있어서 Solaris 파일시스템에 있는 Disk Label 읽어 들여야 하며, 이렇게 읽은 Disk Label 위에 Sparc 차이를 보인다.

- 파티션 저보를 한곳에 모두 저장한 구조가 i386 Disk Label이라고   있다.

- 파티션 데이터는 72번지부터 192Byte 걸쳐 저장되며  16개의 파티션을 저장할  있다.

 

[ i386 Solaris Disk Label ]

 

[파티션들의 특징 비교]

구분

위치

크기(Byte)

OS

부트코드 위치

Dos Partition Table

0 섹터 446번지

64

MS-DOS

Windows 

0 섹터의

0~445번지

Apple Partition Map

1 섹터

512X파티션  섹터 

Mac OS X

보드의 펌웨어 

BSD Disk Label

1 섹터

512

Open BSD,

Free BSD,

Net BSD 

0, 2~16 섹터

Solaris Disk Label

Sparc : 0 섹터

i386 : Solaris 파일시스템 기준 0 섹터

512

Solaris

Sparc : 1~15 섹터

i386 : Solaris Boot Partition

 

- BSD Solaris OS 경우 서버시스템을 겨냥해서 만들어진 OS이다. 따라서 연속적인 클러스터 공간을 확보할  있는 가장 간단한 방법인 파티션을 나누는 방법을 사용하므로써 DISK I/O 관련한 성능 향상을 꾀할  있다. 따라서 파티션의 개수가 월등히 많다.

- 이에 반해 DOS 파티션의 경우 생성할  있는 Primary 파티션 개수가 적지만 매우 간단하다.

- Apple 경우 BSD Disk Label DOS 파티션이 가진 복잡성과 개수 제한 등의 단점을 유연하게 개선하였다고   있다.

 

4.DOS 파티션의 구조

 

1) MS-DOS 파티션 테이블의 구성

[MBR영역 내의 파티션 테이블 구조도]

위치(Byte)

크기(Byte)

설명

0

446

부트코드

446

16

파티션 #1

462

16

파티션 #2

478

16

파티션 #3

494

16

파티션 #4

510

2

MBR Signature(0xAA55)

 

[파티션 테이블 항목]

 

 

2) DOS 파티션 영역의 분석

- 파티션의 실질적인 위치 정보를 읽고 기록할  필요한 부분이 CHS 모드와 LBA 모드이다.

 

이름

Bootable Flag

위치

(Offset)

0

크기

(Size)

1 Byte

일반적인 

(Value)

0x80(부팅가능),

0 

설명

부팅 가능한 하드디스크를 나타내는 플래그. 모든 파티션에서 필수적으로 필요하지 않으며 부팅 가능한 파티션에만 0x80 코드(8bit  MSB ON) 설정 되면 된다.

 

이름

Starting CHS Address

위치

(Offset)

1~3

위치

(Offset)

3Byte

일반적인 

(Value)

가변적

설명

CHS 모드로 표현하는 파티션의 시작 번지 .

24bit  Cylinder 10Bit, Header 8bit, Sector 6bit 표현.

Starting CHS Address Starting LBS Address   입력하지 않아도 되며   하나만 입력하여 사용한다. OS또는 그에 해당하는 어플리케이션에 따라 다르다.

 

이름

Partition Type

위치

(Offset)

4 

크기

(Size)

1 Byte

일반적인 

(Value)

아래 파티션 타입 참고

설명

파티션의 타입을 나타내는 고유 값으로 현재까지 나온 모든 파티션들은 각각 고유 값이 있다. 파티션 타입의 경우 파일 시스템의 종류에 따라 다르기 때문에 만약 파티션을 사용하나 파일시스템을 사용하지 않는 경우 0으로 채워진다.

 

 

이름

Ending CHS Address

위치

(Offset)

5~7 

크기

(Size)

3 Byte

일반적인 

(Value)

가변적

설명

CHS 모드로 표현하는 파티션의  지점. 앞서 나온 Starting CHS Address 매치되어 같이 기록되어야 한다.

 

 

이름

Starting LBA Address

위치

(Offset)

8~11 

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

LBA 모드로 표현하는 파티션의 시작 번지. 0부터 시작하며 LBA 모드를 사용할지 CHS 모드를 사용할 지는 전적으로 어플리케이션에 의존한다.

 

 

이름

Size in Sector

위치

(Offset)

12~15 

크기

(Size)

4 Byte

일반적인 

(Value)

가변적

설명

파티션에서 사용하는 LBA  개수를 의미하며, 하나의 LBA 블록은 512Byte이므로 해당 파티션의  용량은 Size In Sector X 512byte 이다.

 

- CHS모드나 LBA 모드는 어플리케이션에 의존적이라는 것을 기억하자. 또한 Partition Type 정의되어 있는 값들은 파티션 종류뿐 아니라 사용하는 모드가 어떤 모드인지 정의 하고 있으니 어플리케이션 개발  다른 OS 또는 제품과 호환성을 갖고 싶다면 해당 OS에서 읽어 들일  있는 Partition Type 맞게 선택하여야 한다.

 

Partition Type 

설명

0x00

Empty 

0x01

FAT12 primary partition or logical drive, CHS 

0x04

FAT16 partition or logical drive, CHS 

0x05

Microsoft Extended partition, CHS 

0x06

BIGDOS FAT16 pa띠ion or 1얘ical drive(33MB - 4GB), CHS

0x07

Instaliable FileSystem(NTFS partition or logical drive) 

0x0B

FAT32 partition or logical drive, CHS 

0x0C

FAT32 partition or logical drive using BIOS INT 13h extensions, LBA

0x0E

BIGDOS FAT16 partition or logical drive using BIOS INT 13h extensions, LBA 

0x0F

Extended partition using BIOS INT 13h extensions, LBA 

0x11

Hidden FAT12. CHS 

0x14

Hidden FAT16. 16MB - 32MB. CHS 

0x16

Hidden FAT16. 32MB - 2GB. CHS

0x1B

Hidden FAT32, CHS 

0x1C

Hidden FAT32. LBA 

0x1E

Hidden FAT16, 32MB - 2GB, LBA 

0x42

Microsoft MBR, Dynamic Disk 

0x82

Solaris x86 

0x82

Linux Swap 

0x83

Linux 

0x84

Hibernation 

0x85

Linux Extended 

0x86

NTFS Volume Set 

0x87

NTFS Volume Set 

0xA0

Hibernation 

0xA1

Hibernation 

0xA5

FreeBSD 

0xA6

OpenBSD 

0xA8

MacOS X 

0xA9

NetBSD 

0xAB 

MacOS X Boot 

0xB7 

BSDI 

0XB8 

BSDI swap 

0xEE 

EFI GPT Disk 

0xEF 

EFI System Partition 

0xFB 

Vmware FileSystem 

0xFC 

Vmware swap 

 

-  표의 경우 Partition Type 해당하는 미리 정의된 타입니다. 종류가 정말 많은걸   있다.

 

 

(개인 노트북 파티션 테이블 항목)

 

위의 파티션 테이블 분석

분석내용

2진값

Boot Flag : 80

부팅 가능 파티션

1000 0000

Starting CHS Addr : 20 21 00

[Little Endian]

00 21 20

0000 0000 0010 0001 0010 0000

Cylinder(10bit)

Header(8bit)

Sector(6bit)

Part Type : 07

0000 0111

0x07 : NTFS Partition or Logical Dirve) 

Ending Chs Addr : DF 13 0C

[Little Endian]

0C 13 DF

0000 1100 0001 0011 0000 1100

Cylinder(10bit)

Header(8bit)

Sector(6bit)

String LBA Addr : 00 08 00 00

[Little Endian]

00 00 08 00 (2048)

0000 0000 0000 0000 0000 1000 0000 0000

Size in Sector : 00 20 03 00

[Little Endian]

00 03 20 00

=> 204800(Sector) X 512(byte)

= 104857600 byte

104857600/1024 = 102400 Kbyte

102400/1024 = 100 Mbyte

0000 0000 0010 0000 0000 0011 0000 0000

 

 

3) MS-DOS 확장 파티션

- MBR 영역 내의 파티션 테이블에 4 까지 파티션을 가질  있으나 추가로 확장을 해야  경우 확장 파티션(Extended Partition) 사용한다.

-  파티션(Primary Partition)  마지막으로 만들어진 파티션이 확장파티션에 해당하며 확장 파티션 영역 안에서 추가로 영역을 나눠야 하기 때문에 MBR 영역 내의 파티션 테이블만 가지고서는 확장 파티션의 레이아웃을 한눈에 알아보기 힘들다.

-  확장 파티션의 계층구조로 인해 다른 파티션에 비해 구현의 번거로움이 있다.

 

[확장 파티션]

 

-  파티션 내에 존재하는 확장 파티션을  확장 파티션(Primary Extended Partition)이라고 한다.

-  확장 파티션을 포함한 모든 확장 파티션은 추가 확장 파티션을 가질  있으며 파티션 개수는 제한이 없어진다.

- 확장파티션에서 파티션을 검색하는 경우 MBR 같은 역할을 하는 것이  파티션 내의 BR(Boot Record)이다.

- 파티션 테이블 역시 MBR 같이 BR 446번지부터 기록되어 찾아가게 된다.

- 이때 확장 파티션의 시작위치(String LBA Address) 주의 해야 한다.  파티션 영역의 시작 위치들은 디스크의 시작 위치인 0 섹터를 기준으로 기록 되지만, 확장 파티션의 내의 파일시스템의 시작 위치는 각각 확장 파티션 별로 시작 위치가 정해지게 된다.

 

[DOS 확장 파티션  파일 시스템 기준]

 

- LBA 모드를 기준으로  확장 파티션이 1000번째 섹터 위치라고   확장 파티션에 존재하는 1번째 파일 시스템은 해당 확장 파티션의 시작 주소를 기준으로 위치와 크기를 지정하게 된다.  1000 섹터 기준으로 64번째 섹터부터 파일시스템이 위치하며, 크기가 3000개의 섹터의 걸쳐 기록 하고 있다.

- 2번째, 3번째 역시 각각의 확장 파티션을 기준으로 시작 위치가 정해지는 것을 확인   있다.

- 그러나 파일 시스템이 64번째부터 시작 되는지 의문이  것인데 0~63번째 까지는 예약된 영역으로 현재 사용되지 않다. 파일 시스템에 따라서 unallocated 영역의 크기가 달라지기도 하기  문에 BR 파티션 테이블을 참조하여 파일시스템 영역으로 이동해야 한다.

- unallocated 영역은 0으로 채워져 있기도 하지만 쓰레기 값이 있을 수도 있으니 데이터로 오해 하지 말도록 하자.

-  파티션에서 사용되는 시작 섹터의 기준과  확장 파티션의 경우와  확장 파티션의 경우가 다른 부분을  이해 해야  것입니다.

-  확장 파티션의 시작 섹터 기준은 저장장치의 0 섹터(절대 위치)

-  파일 시스템이 시작되는 섹터 위치의 기준은  파일 시스템을 포함하고 있는  파티션의 시작 섹터

 

5. 파티션 분석 실습

 

관리자 권한으로 실행을   아래 소스를 입력하면 하드디스크의 파티션 정보를   있게 됩니다.

#include <Windows.h>

#include <stdio.h>

 

typedef unsigned char U8;

typedef unsigned short U16;

typedef unsigned int U32;

 

//섹터 내 파티션 테이블 번지

#define PARTITION_TBL_POS 0x1BE

//확장 파티션 타입

#define PARTITION_TYPE_EXT 0x0F

 

typedef struct _PARTITION  //확장 파티션 타입

{

    U8 active;

    U8 begin[3];

    U8 type;

    U8 end[3];

    U32 start;

    U32 length;

}PARTITION, *PPARTITION;

 

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

 

//디스크 내의 모든 파티션 검색

U32 GetLocalPartition(U8 Drv, PPARTITION pPartition);

//확장 파티션 검색(재귀 호출)

void SearchExtPartition(U8 Drv, PPARTITION pPartition, U32 *pnPartitionCnt);

//디스크 내의 부트 레코드에서 파티션 테이블 저장

void GetPartitionTbl(U8 Drv, U32 nSecPos, PPARTITION pPartition, int nReadCnt);

//파티션 테이블의 속성 출력

void PrintPartitionInfo(PPARTITION pPartition, U32 nIdx);

 

U32 main(void)

{

    PARTITION PartitionArr[50]; //디스크 내에 파티션 정보를 담는 배열

    U32 nPartitionCnt=0;

    U32 i;

 

    memset(&PartitionArr, 0, sizeof(PARTITION) * 50);

    nPartitionCnt = GetLocalPartition(0,PartitionArr); //0번 물리디스크 내에 존재하는 DOS파티션 읽기

 

    for(i=0; i<nPartitionCnt ; i++) //파티션 정보 출력

        PrintPartitionInfo(&PartitionArr[i], i);

 

    return 0;

}

 

U32 GetLocalPartition(U8 Drv, PPARTITION pPartition)

{

    U32 i;

    U32 pPartitionCnt=0;

    PPARTITION pPriExtPartition = NULL;

 

    //주 파티션이므로 4개의 파티션 읽어옴

    GetPartitionTbl(Drv, 0, pPartition, 4);

    for(i=0;i<4 && pPartition->length !=0; i++){

        if(pPartition->type ==PARTITION_TYPE_EXT) {

            pPriExtPartition = pPartition;

        }

        pPartition++;        //다음 파티션으로 이동

        pPartitionCnt++;    //파티션 카운트 UP

    }

 

    if(!pPriExtPartition)

        return pPartitionCnt;

 

    SearchExtPartition(Drv, pPriExtPartition, &pPartitionCnt);

    return pPartitionCnt;

}

 

void GetPartitionTbl(U8 Drv,U32 nSecPos, PPARTITION pPartition, int nReadCnt)

{ //MBR 및 BR을 읽어 들여 파티션 테이블을 버퍼에 저장하는 함수

    U8 pSecBuf[512];

 

    //물리적 디스크 Drv의 nSecPos번 센터에서 1개의 블록을 읽어온다.

    if(HDD_read(Drv, nSecPos, 1, pSecBuf) ==0) {

        printf("boot sector read failed \n");

        return ;

    }

    //파티션 테이블은 446번지에 있으므로 PARTITION_TBL_POS만큼 포인터 이동

    memcpy(pPartition, (pSecBuf + PARTITION_TBL_POS), sizeof(PARTITION) * nReadCnt);

}

 

void SearchExtPartition(U8 Drv, PPARTITION pPartition, U32 *pnPartitionCnt)

{

    int nExtStart = pPartition->start;

    //데이터를 읽어오기 위해 포인터를 다음 파티션 번지로 이동

    pPartition++;

    //부 파티션과 확장 파티션이 있을 수 있으므로 2개의 파티션을 읽어옴

    GetPartitionTbl(Drv, nExtStart, pPartition, 2);

    while(pPartition->length !=0) {

        (*pnPartitionCnt)++;

        if(pPartition->type == PARTITION_TYPE_EXT) {

            SearchExtPartition(Drv, pPartition, pnPartitionCnt);

        }

        else

            pPartition++;

    }

}

 

void PrintPartitionInfo(PPARTITION pPartition, U32 nIdx) //파티션 정보 출력

{

    fprintf(stdout, "[PARTITION #%d]\n", nIdx);

    fprintf(stdout, " Bootable : 0x%X\n", pPartition->active);

    fprintf(stdout, " Type : 0x%X\n", pPartition->type);

    fprintf(stdout, " start : %d\n", pPartition->start);

    fprintf(stdout, " Length : %d\n", pPartition->length);

    fprintf(stdout, " Partition Size : %d MB\n", pPartition->length/1024*512/1024);

    fprintf(stdout, "=========================\n\n");

}

 

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;

}

발표자: 나

주제: 파일시스템

내용:

- 파일시스템 개요

- FAT16 소개

- FAT16 프로젝트 실습 소개

'Seminar' 카테고리의 다른 글

[ENC] 세미나 3회 - 머신러닝  (0) 2020.04.10
[ENC] 세미나 2회 - BUS IP 설계  (0) 2020.04.10
[ENC] 세미나 1회  (0) 2020.03.19

+ Recent posts