내용

 

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;

}

https://m.blog.naver.com/bitnang/70183421214

내용

 

1.파일시스템 소개

[파일시스템의 정의]

[파일시스템은 저장장치 내에서 데이터를 읽고 쓰기 위해 미리 정해진 약속] 이라고   있다.

 미리 정해진 약속, 규약이라고 보면 될것으로 생각 된다. 네트워크에서는 프로토콜의 경우를 떠올려보자.

간단히 예를 들어 보도록 하자. RPG게임을 한다고 했을  게임을 진행하기 위해 게임을 진행하다가 세이브를(저장) 하게  것이다. 세이브 파일에는 케릭터의 종류, 레벨, HP, MP, 현재위치,  등이 기록되어야 불러올  이전에 진행했던 위치부터 게임이 진행  것이다.

  필요한 데이터를 어떻게 저장할까?

 

[데이터 저장 규약의 ]

파일에 데이터를 저장하기 위한 방법의 예로서  6종류 데이터를 12Byte 공간에 위치와 크기에 대한 약속을 정해서 저장한 것이다.

종류가 늘어나게 된다면 같은 파일 뒤에 위의 규약대로 이어서 저장을 하거나 아니면 다른 파일에  같은 방식으로 저장을 하면  것이다.

 

파일 시스템 역시 위와 같이 저장할 데이터를 결정하고,  크기와 위치 등을 미리 약속한  운영체제 등에서 그대로 사용하면 된다.

물론 실제 파일시스템의 데이터 구조는 방대하고, 복잡하겠지만 결국 '미리 약속한 방법으로 저장하고 읽어 들이는 '이다. 따라서 너무 어렵게 생각 하지 말고 진행하도록 하자.

 

[파일시스템 선택의 필요성]

수많은 파일 시스템  내가 사용하기 위한 용도에 맞는 파일시스템의 선택에 대해 알아보자.

파일시스템들의 특징이 서로 다른  처럼 각각의 용도에 따라 선택을 달라지게 하는 것이 좋다.

2.파일시스템 분류

1)일반적인 파일시스템

 

가장 일반적인 파일시스템은 디스크(플로피, 하드디스크) 같은 저장장치에서 주로 사용된다. 앞으로 계속 자세하게 분석할 파일시스템들이지만 여기서  종류와 간략한 구조를 알아보자.

 

[FAT(File Allocation Table) 파일시스템]

- 마이크로소프트사의 빌게이츠가 만들었으며  세계적으로 가장 많이 사용되는 파일시스템

- 초기에 만들어졌으며 여러  발전을 거듭해 왔지만, 최초 제작 당시에는 고려하던 저장장치의 크기가 매우 작았으며 성능상 문제는  이슈로 작용하지 않음.

- 매우 단순한 구조를 지니고 있으며 최근엔 대용량 저장을 위해 FAT16, FAT32 등이 만들어진 이후 윈도우 OS 흥행과 더불어 지금도 널리 사용되고 있다.

- FAT파일시스템의 범용성은 휴대용 장치들과 PC와의 호환성을 높여주는 결과를 가졌으며, 이동식 저장장치들은 FAT파일시스템을 설치하기만 하면 별도의 설치과정 없이 엔드유져(End User)들의 PC에서 간편하게 읽어 들일  있게 만들어 .

- 파일시스템에서 사용되는 부가 기능은 적고, 제약사항들은 많은 단점이 있으나 그만큼 가볍고 심플하다.

- 단점으로는 연결 리스트를 사용한 자료구조로 인해 검색시간이 오래 걸리고, 파일 데이터 블록들이 여기저기 흩어지는 단편화 현상이 심해져 한파일의 데이터를 읽어 들이는 데도 디스크 헤드가 여러  이동하게 만들어짐.

- 디스크 조각 모음 등의 부가적인 프로그램이 등장하였지만 근본적인 해결책이 되지 않아 서버 시스템 등에서 사용되기에는 여러 가지 부족함이 많은 파일시스템이기 때문에 이후 여러 파일시스템들이 이를 개선하기 위해 등장하게 .

 

[HPFS(High Performance FileSystem)]

- IBM OS/2 1.2 부터 사용된 파일시스템이며 NTFS 나오기 까지 많은 영향을  파일시스템.

- 제작 당시부터 대용량 디스크에 적합한 구조를 지니고 있으며, 효율적인 캐싱과 FAT파일시스템에 비해 파일 손실과 단편화가 적고, 서버 시스템에 사용할  있도록 여러 가지 보안 기능 등에 대한 요구를 충족시켜   있는 파일 시스템이다.

- 대용량 저장 장치를 타켓으로 하였기 때문에 200MB 미만의 저장장치에서는 성능저하를 가져올  있는 단점이 있으며, 섹터 크기가 512Byte 고정되었기 때문에 기본 데이터 I/O 단위를 변경할  없다.

- OS/2 윈도우 NT와의 경쟁에 밀려 흥행에 실패하였고, 윈도우 NT 4.0부터는 HPFS 지원하지 않아 호환이 불가능하여 우수한 성능  발전 가능성에도 불구하고 비운의 파일시스템이 .

 

[NTFS(New Technology FileSystem)]

- 마이크로소프트사의 서버급 운영체제인 Windows NT 사용되는 파일시스템.

- 윈도우 NT  2000 이상의 OS에서 대표적인 파일시스템으로 자리 잡아 서버 시스템은 물론 일반 PC에서도 널리 사용되고 있다.

- NTFS 대용량 저장장치를 겨냥해서 제작 되었으며, 높은 안정성과 부가기능 지원하고, FAT HPFS 있던 여러 제약 사항들을 크게 개선한 파일 시스템이다.

- 제작사인 마이크로소프트사에서 전체 스팩을 공개하지 않아 완벽한 분석이 이루어지지 않았으며, 이로 인해 리눅스 등의 다른 OS에서 NTFS 지원 한다고 해도 미흡한 부분이 있을 수밖에 없다.

 

[UFS(Unix FileSystem)]

- 유닉스의 대표적인 파일시스템으로 현재까지 쓰이는 대부분의 유닉스에서 사용되는 파일시스템의 근간이 .

- BSD계열(FreeBSD, NetBSD, OpenBSD ) 물론 HP-UX, Apple OS X, Sun Solaris 이르기 까지 많은 유닉스 계열의 OS들이 UFS 각각의 OS 맞게 변형해서 사용하고 있다.

- UFS 빠른 속도와 높은 안정성을 목표로 만들어짐.

- 저장장치를 그룹화하여 관련된 데이터끼리는 최대한 가까운 위치에 자리할  있는 구조로 되어 있어 디스크 헤드의 이동이 비교적 적고, 중요한 데이터는 이런 그룹에 걸쳐 많은 백업을 저장하므로 만일의 사태에 대하여 보다 신뢰성을 높임.

- 미국 Berkeley 대학의 FFS(Fast FileSystem)에서 근간을 이루었으며, Bell 연구소에서 Unix Version7 개발할 때부터 본격적으로 UFS라는 명칭을 사용하였다. UFS 리눅스 파일 시스템인 Ext2   영향을 미치게 된다.

 

[Ext2파일 시스템(Second Extended FileSystem)]

- 현재 리눅스의 기폰 파일시스템인 Ext3에서 저널링 기능을  파일 시스템으로서 UFS 근간으로 하고 있다.

- UFS에서 유명무실한 구조들은 제거하고, 전체적인 구조를 보다 간략히  Ext2 비교적 명료하고 간단하면서 UFS 속도와 안정성을 고류 갖춘 파일 시스템이다.

- 현재까지도 리눅스 기본 파일시스템으로 자리잡고 있는 Ext3에서 그대로 사용 되고 있다.

 

2)플래시 파일 시스템

- 플래시 파일시스템(Flash FileSystem) 특징은 플래시 메모리의 특성에 최적화되어 있다.

- 플래시 메모리는 블록(Block)구조로 되어 있는데 읽는 것은 블록과 관계없이 바이트 단위로 자유롭지만, 쓰거나 지우는 것은 언제나 블록 단위로 해야만 한다. 이런 블록의 크기는   편이여서 보통 64~128kb까지 된다.

- 플래시 메모리의 특징은 블록에 쓰기를   있는 횟수에 제한이 있다. 보통 10,000~100,000 정도를 수명으로 본다.  수명은 플래시 메모리의 수명이 아니라 블록의 수명이다.

- 만약 어떤 플래시 메모리에 블록이 10개고, Write 횟수가 100,000 경우  플래시 메모리에 블록 하나에 충분히 들어갈 만한 크기의 작은 데이터 하나만 기록한다고 했을   경우 블록을 돌려가면서 데이터를 쓰게 되는데  플래시 메모리의 수명은 100,000 X 10블록 = 1,000,000번이  것이다.

- 임베디드 시스템에 담긴 플래시 메모리의 구조에 호환성은 별로 중요하지 않기 때문에 자체적으로 필요한 구조의 플래시 파일 시스템을 구현하여 제품에 올릴 경우가 만을 것인데 이때 임베디드 리눅스를 사용하게 된다면 JFFS2 YAFFS 파일시스템을 주로 이용하게 된다.

 

3) 네트워크 파일 시스템

- 네트워크 파일시스템(Network FileSystem) 1984 Sun Microsystems에서 원격에 위치한 파일시스템을 로컬 파일시스템처럼 이용할  있도록 개발한 프로토콜이다.

- 단순히 파일 공유 차원의 것이 아니라, NFS 결국 파일 시스템임을 인지 해야 하기 때문에 원격 파일시스템이 마운트되면 마운트 지점이 아래 위치한 파일에 접근을 하는 경우 NFS 파일시스템 레벨에서 시스템 콜을 받아 직접 네트워크 파일을 수신하여 로컬 영역에 파일을 쓰거나 직접 실행할  있도록 한다.

- NFS 초기 버전에서는 UDP 통신만 가능 했으나 이후 TCP 지원하며, 파일 크기를 64bit까지 지원하여 4GB 넘는 파일도 NFS 이용 가능 하다.

 

4) 가상 파일 시스템

- OS 차원에서 가상 파일시스템(Virtual FileSystem)이라는 상위 레벨의 파일시스템 인터페이스가 존재하기 때문에 응용프로그램에서는 아무 구분 없이 OS  시스템 콜을 호출하면 커널은 미리 등록되어 있는 파일시스템 함수를 호출하여  종류에 상관없이 같은 결과를   있다.

- 만약 가상 파일시스템이 없다면 일단 OS 설치하고 나면 다른 파일시스템이 설치된 파티션을 인식할  없을 뿐만 아니라 다른 파일시스템의 인식을 위해서 별도로 컴파일  파일시스템 모듈을 덮어씌워야  것이다.

 

[가상 파일 시스템의 레이어구조]

 

- 가상 파일 시스템은 로컬 영역이나 원격 영역의 파일시스템을 실제로 제어하지 않기 때문에 OS 부팅 시에 시용 가능한 파일시스템 함수를 가상 파일시스템 쪽으로 등록해 주어야 한다. 일반적으로 하드디스크에서 사용하는 파일시스템뿐  아니라 CD-ROM 파일시스템과 네트워크 파일시스템  기본적으로 등록하는 파일시스템이 여러 가지 있다.

- 가상 파일시스템의 아이디어가 최초 적용된 OS 1986  Sun Microsystems  SunOS 2.0 이다. 당시 SunOS 에서 로컬 영역의 UFS Remote 영역의 NFS 통시에 지원하면서  개념이 도입되었고, 이후 대부분의 유닉스 계열의 OS들이 가상파일시스템을 지원하였다.

 

3. 파일시스템 요소들

1)기본요소

- 파일시스템은 커널 또는 응용프로그램에서 저장하고자 하는 데이터를 저장장치에 기록하기 위한 방법을 제시하므로 이에 필요한 저장장치  논리적 요소들을 알아볼 필요가 있다.

 

[클러스터]

- OS 파일시스템 생성  저장장치의 크기를 고려하여 클러스터의 크기를 조절한다.

- 저장장치의 크기  사용 용도에 따라서 달라져야 한다.

- OS 의해 데이터를 읽고 쓰는 과정에서 파일시스템은 미리 정해져 있는 클러스터의 크기를 기본단위로 하여 읽고 쓰는 과정에서 파일시스템은 미리 정해져 있는 클러스터의 크기를 기본단위로 하여 입출력을 하게 된다.

- 클러스터의 크기가 4096Byte라면 1Byte 읽더라도 4096Byte 읽어야 한다. 단순히 읽는 거라면 문제가 되지 않지만 쓰는 경우에는 문제가   있다.

 

- 클러스터 사용영역

-  그림처럼 크기가 작은 파일을 저장할 경우 낭비되는 영역이 생기는데  부분의 공간은 사용이 불가능 해진다.

- 위처럼 낭비되는 공간이 있음에도 클러스터의 크기를 정하는 이유는 성능적인 측면 때문에 있다. PC사용시 처리속도에 있어 가장  비용을 요구하는 작업은 디스크 입출력이다.

- 만약 10KB 파일을 읽을 경우 클러스터가 1KB라면 10 I/O 발생해야 하지만, 클러스터 크기가 4KB라면 3번의 I/O 읽을  있을 것이다.  경우 2KB가량이 낭비되지만 요즘같이 대용량 하드라면 무시할  있을만한 정도이다.

 

- 파일시스템 역량과 저장장치 크기에 따라 클러스터의 크기가 저장장치 사용에 중요한 요소가   있다. 예를 들어 FAT16 같이 파일시스템 주소 지정 방식에 16Bit 사용하는 상황이라면 클러스터의 크기에 따라 최대 사용할  있는 저장장치의 공간이 차이를 보일  있다.

    - 클러스터의 크기가 1KB라면 저장장치의 크기는 65,536(216) X 1,024(1KB) = 64MB

    - 클러스터의 크기가 4KB라면 저장장치의 크기는 65,536(216) X 4,096(4KB) = 256MB   것이다.

 

- 32Bit, 48Bit, 64Bit  다양하게 사용되지만, 저장장치의 크기가 작고 가격이 비싸던 시절에는 상황에 따라 충분히 문제가   있다.

- 일반적으로 클러스터의 크기는 디스크를 포맷할 경우나 파일시스템을 생성하는 시점에 지정할  있으며, 최근의 대용량 하드에서는 주로 4KB 크기가 기본으로 지정되어 있다. 플로피의 경우 기본 1KB 지정되어 있다.

 

[파일]

- 파일시스템이란 결국 파일을 기록하기 위한 것이므로 파일을 이루는 구조와 그것을 관리할  있는 추가적인 방법을 제시하는 것이다.

- 파일은 속성을 기록하는 메타 데이터 영역, 실제 데이터를 기록하는 데이터 영역으로 나눌  있다.

- 메타데이터는 파일시스템에서 파일을 관리할  있는 정보 자체를 말하며 파일의 속성을 물론 실제 데이터를 기록한 위치를 찾아가기 위한 정보를 포함하며, 파일 시스템들은 이런 정보들을 어딘가에 저장해두고 OS 정보 요청  해당 파일을 찾아서 정보를 조합하여 넘겨주면 사용자가 파일이라고 부르는 정보가 되는 것이다.

 

- 파일 정보 요청 프로세스

- 파일 데이터는 클러스터 단위로 읽고   있으며,  1Byte라도 사용한다면 1개의 클러스터 영역이 사용된다. 최근 하나의 클러스터에 여러 파일 정보를 담을  있는 연구가 진행중이며, 현재 스펙상 이것이 가능한 파일시스템도 있지만, 실제 사용되는 경우는 없다.

- 모든 파일시스템은 파일 정보를 관리하는 자료구조를 가지며,  자료구조에 의해 파일시스템의 성능에  영향을 끼치게 된다.

- FAT 경우 연결리스트, NTFS B-Tree 사용하며,  알고리즘의 특성과 마찬가지로 검색기능에 있어 NTFS FAT 비해 효율이 좋은 것처럼 여러 가지 차이를 보인다.

 

[디렉토리]

- 파일들을 계층화하고 그룹화   있는 개념으로, , 하위 개념의 디렉토리와 파일들은 관리하는데 있어 많은 이점을 제공하며, 때로는 이런 개념을 뛰어넘기 위한 링크(Link) 등의 방법을 제공하는 파일 시스템도 있다.

- 파일과 디렉토리에 대한 속성 정보를 따로 만들어 놓고 이를 통해 파일과 디렉토리를 구분하여 관리하지만, 실제 안을 들여다 보면 결국 비슷하다.

- 파일의 경우 데이터 영역에 있어 파일 정보에서 그것의 위치를 가리키며, 디렉토리의 경우도 데이터 영역이 존재하는데  영역에는 하위 존재하는 디렉토리  파일 리스트 들이 있다.

- 가장 상위 디렉토리의 경우 파일시스템에 따라서 관리하는 방법이 다르므로 해당 파일 시스템의 분석을 살펴보도록 해야한다.

 

2)부가요소

[소유권]

- 개인용 컴퓨터와 서버용 컴퓨터에서 사용하는 파일시스템간의 두드러진 차이점 중의 하나는 파일마다 그룹과 소유권을 따로 관리할  있다는 점이다.

- 한대의 컴퓨터에 여러 명의 사용자가 접근하는 경우  사용자들의 파일에 대한 접근 권한을 따로 관리할 필요가 있으며 시스템 관리자의 입장에서도 사용자들이 아무 제제 없이 시스템 파일에 접근하는 것을 막아야 한다.

- FAT파일시스템에서는 소유권을 관리할  없기 때문에 OS차원에서 제한을 두는 것은 한계가 있다.(Win 98 경우 사용자 계정을 달리 로그인할  있지만, 정작 그들간의 접근할  있는 파일에는 제한이 없다.)

- NFTS EXT2 비롯한 서버급 파일시스템은 모든 파일에 사용자 그룹과 소유권한을 부여할  있어 사용자가 시스템 파일에 허가 없이 접근하는 것을 막을  있다.

 

[동기화]

- 현재 모든 OS들은 멀티태스킹(Multi-Tasking) 기능을 지원하여, 하나의 CPU에서도 여러 프로그램이 동시에 실행되는 것처럼 동작한다.

- 이때 주의할 점이 동기화 작업이다. 하나의 파일에 여러 프로세스가 동시에 접근해서 작업을 하는 경우 파일에 (Lock) 걸어주고, 적절한 시점에 해제하는 동기화가 진행되어야 하는데, 파일시스템을 제작하면서 동기화만큼 손이 많이 가는 작업이 드물다.

- 여러 가지 상황에 따른 고민과 여러 가지 상황의 문제가 생길  있기 때문에 많은 시간이 소요된다.

(만약 A라는 파일에 삭제 명령을 하고 동시에 A라는 파일을 읽기 명령이 진행 되었을 경우 A라는 파일이 락이 걸려있지 않다면 삭제되고, 읽을때 잘못된 포인터 접근하여 세그먼테이션 오류를 발생 시킬  있다.)

- 최근에 사용되는 파일시스템들은 대부분 파일에 락을   있도록 스펙이 정의되어 있는데 락에는  유형과 적용 해야하는 위치가 다양하기 때문에 파일시스템을 제작할 경우 각별히 신경써서 구현해야 한다.

 

[일관성(Consistency) 체크와 저널링]

- 예기치 못한 상황에서 시스템이 멈추거나 정전이 일어날 경우 문제가 발생하는데 파일을 쓰고 있었다거나, 중요 업데이트를 하고 있을 경우 실제 파일이 없는데 있는 것처럼 표시되어 데이터가  못될  있는  일관성이 깨지게 된다.

- 가장 우선적으로 파일시스템 구현  이런 상황을 대비 해야하며, 파일을   메타데이터와 파일 데이터를 쓰는 순서를 시스템 Crash 상황을 고려 해야한다.

- 메타데이터를 먼저 쓰고, 파일 데이터를 쓰게  경우 파일 데이터 쓰는 도중 시스템 중단되면 파일이 있는 것처럼 보이겠지만 실제론 없을 것이다. 그러나 반대로 파일 데이터를 먼저 쓰고 메타데이터를  경우 시스템이 중단이 되어도 메타데이터가 나중에 쓰여지기 때문에 파일리스트에 없을 것이며, 해당 파일데이터가 쓰여진 곳은 어차피  영역이고, 나중에 다른 데이터가 쓰여지게  것이다.

- 파일시스템 일관성 문제가 발생했을 경우 별도의 유틸리티 프로그램이 요구된다. chkdsk.exe(FAT, NTFS), e2fsck(Ext2) 등이 있다.

- 이런 프로그램은 파일시스템 내에 존재하는 모든 메타 데이터를 검색하여 문제가 있는 부분은 수정한다. 그러나 요즘 같은 경우 용량이 크기 때문에 시간이 많이 걸릴 수도 있다. 이런 상황을 위해 대두된 것이 저널링 기능이다.

- 저널링은 데이터베이스에서 일관성 체크를 위해 사용되는 방법을 파일시스템에 적용한 것으로 파일시스템 업데이트 시에 로그를 기록하고, 문제가 생길 경우 해당 로그를 참조하여 업데이트를 취소할  있고, 파일시스템에 적용완료(commit)  있다.

- 저널링 기능의  장점은 업데이트 로그를 기록하고 있기 때문에 일관성 체크를 위한 파일시스템의  영역을 검색하지 않아도 되며, 해당 로그만 찾아가서 작업을 수행하면 된다. 따라서 시간이 많이 단축될 것이다.

 

[보안]

- 파일의 소유 권한과 실행 권한 등을 다루는 방법.(OS등을 통해 파일시스템에 접근하는 상황에 해당될뿐 물리적으로 접근시 데이터가 들어남)

- 파일시스템 암호화 기법으로 암호화를 적용하는 계층은 가상 파일시스템과 파일시스템 자체에서   있다. 또는 메타 데이터만을 암호화 하는 경우와 파일 영역까지 압축을 통한 암호화를 진행하기도 하며, 디렉토리  파일 등을 선택해서 적용할  있다.

 

4. 저장장치

[저장장치 소개]

- 저장장치는 주기억 장치인 메모리와 구분을 위해 보조기억장치라고 불리며, 여기서는 보조기억장치대신 저장장치라는 용어로 진행한다.

- 장치간의 속도와 용량 관계

- 컴퓨터는 전원이 나갈 경우 기억하던 데이터를 잃어버리는데 이를 대비하여 저장장치(보조기억장치) 저장해 놓는다.

- 주기억장치인 메모리의 경우 저장장치에 비해 값이 비싸기 때문에 상대적으로 작은 양에 데이터들을 담고, 해당 데이터의 처리가 끝나고 다른 데이터를 처리할 경우 저장장치로부터 읽어 들이는 방법을 사용한다.

- CPU입장에서 저장장치들은 너무 느리기 때문에 속도가 상대적으로 빠른 메모리에 적재한  CPU 처리하는 식으로 진행이 된다.

- 저장장치로부터   데이터를 읽어서 메모리에 올렸다면 최대한 저장장치를 접근을 하지 않는 것이 좋으며, CPU 메모리에 올린 데이터를 최대한 적게 접근하여 데이터들을 처리하는 것이 좋다.

 

[저장장치 분류]

- 시간이 지나면서 저장장치의 종류와 형태는 매우 다양해졌다.

- 저장장치(Storage Device) 분류하는 방식에는 여러 방법이 있지만, 데이터를 저장하는 방식에 따라 분류를 해보자.

 

1)자기(Magnetic) 방식

- 자기적인 특성을 이용하여 데이터를 저장하는 방식으로 종이를 이용한 천공카드(Punch card) 다음으로 오래된 기술이다. 천공카드의 경우 저장장치 무대에서 사라졌지만, 자기 저장 방식은 구조와 기술의 진보를 통해 다양한 형태로 계속 진화하여 지금까지도 주요 저장 방식으로 사용되고 있다.

- 종류로는 자기테이프, 자기드럼, 플로피, 하드디스크 등이 있다.

- 가장 대중적인 매체는 하드디스크로 대부분의 파일시스템들이 하드디스크에 적용하기 위해 개발되었을 정도이다.

- 저장 방식의 특성상 자기장에 약한 단점이 있다.

 

2) 전기(Electronic) 방식

- 전기적인 특성을 이용하는 저장장치들은 특성상 전부 반도체 범주에 속한다.

- 가격이 비싸고 저장용량도 작아서 주요 저장장치로 사용되지 않다가 최근 플래시 메모리의 비약적인 기술 발전으로 널리 사용되고 있다.

- 다음은 전기적인 특성을 이용하는 저장 장치이다.

매체

설명

PROM

 한번 기록할  있는 ROM

EPROM

자외선으로 지운  기록 가능한 ROM

EEPROM

전기적 특성으로 지운  기록 가능한 ROM

FLASH-ROM

EEPROM보다 진보된 메모리

 

- 사실 PROM, EPROM, EEPROM 같은 제품들은 저장장치(Storage Device)라기 보다는 메모리쪽에 가깝다. 형태가 IC 같이 외부 인터페이스 없이 CPU 연결이 가능하기 때문이다.

- 플래시 메모리의 경우 이런 분류를 모호하게 하는데 특성상 메모리에 가까운 제품이지만 활용도가 높아 이동용 저장장치에 주로 사용된다.

- 기술적인 특성상 생산 단가가 낮고, 집적도를 높일  있어  용량이 가능하였다. 이를 토대로 여러가지 저장장치들이 판매되고 있는데 CF(Compact Flash) Card, Memory Stick, SD Card, USB Memory  다양한 제품이 모두 플래시 메모리를 응용한 것이다.

- 이런 저장장치들은 부피를 매우 줄일  있으며, 외부 충격에 강한 편이다.

- 플래시 메모리는 특성에 최적화된 파일시스템을 올리는 것이 좋은데 JFFS2 YAFFS같은 파일 시스템이 플래시메모리에 최적화된 파일시스템이다.

- 이럴 경우 Windows 호환을 포기해야 하는데 Windows 호환이 주목적이라면 플래시메모리라고 할지라도 FAT 구현하는 것이 좋다.

 

3)광학(Optical) 방식

- 레이저(Laser) 이용하여 빛을 쏘았을  패인 곳과 그렇지 않은 곳에 빛의 반사율이 다른 점을 이용하여 데이터를 읽거나 쓰는 방식이다.

- 자기방식과 전기 방식의 중간쯤 되는 것으로 다른 방식보다 적은 비용이 들고, 흠집이 나지 않는다면 충격에 강하며, 물에 빠져도  말리면  것이다.

- 광학 매체의 특성상 쓰는 속도가 느린 편이라 대량의 데이터를 손쉽게 움직이기에 무리가 있으며, 주로 Backup용으로 사용되는 경우가 많다. 그러나 쓰는 속도가 느리기 때문에 실시간으로 데이터를 읽고 쓰고 하는 용도로 쓰는 경우는 없을 것이다.

- ISO 0660 같은 파일 시스템을 사용하고, CD-RW 지원하는 UDF(Universal Disk Format)같은 파일 시스템도 있다.

 

5.하드디스크 분석

[하드디스크 소개]

- 컴퓨터가 데이터를 저장하는데 가장 일반적인 저장장치이다.

- 가장 많이 사용되는 만큼 파일시스템도 매우 다양하다.

 

[하드디스크의 주요 구성요소]

- 하드디스크 물리 구조에서 실린더, 트랙, 섹터를 주로 다루게 되는데 이는 디스크 읽는 단위로도 사용되기 때문에 알아둘 필요가 있다.

- [하드디스크의 구조]

- 하드디스크의 경우 여러장의 플래터로 이루어져 있고, 표면은 트랙과 섹터로 나눠지게 된다.

- 플래터의 표면마다 헤더가 위아래로 있기 때문에 '플랙터 X2'  것이다. 따라서 헤더의 개수와 표면의 개수가 같기 때문에 주로 헤드의 숫자만 언급하는 경우가 많다.

 

[트랙]

디스크 중심으로 반지름이 같은 영역을 이어놓은 원을 보면  것이다. 반지름이 커질수록 디스크의 가장자리 영역에 데이터를 많이 보관할  있게 된다.

[섹터]

트랙을 자른 형태가 섹터가  것이다.  섹터는 571Byte 공간은 차지한다. 이중 59Byte  섹터에서 고유 번호등을 저장하며 나머지 512Byte 사용자들의 데이터 저장에 사용되는 영역이다. 따라서 통상적으로 섹터의 크기는 512Byte라고 이야기 한다. 디스크에서는 섹터보다 적은 양의 데이터는 처리할  없기 때문에 데이터 기록의 가장 기본단위 쓰여지며 이는 트랙과 함께 가장 중요한 단위이다.

[실린더]

하드디스크의 경우 플래터가 여러  겹칠 것인데  경우 같은 위치에 있는 트랙들을 합쳐서 실린더라고 한다.

(예를 들어 4장의 플래터와 플래터 표면의 100개의 트랙이 있다면 100개의 실린더가 있을 것이고  실린더에는 8개의 트랙이 있을 것이다.)

[헤드]

데이터를 읽고 쓰기 위해 헤드암 가장  부분에 있다.

각각의 플래터 표면에 하나씩 할당되어 있고, 각각의 헤드는 따로 움직이는 것이 아니라 헤드암에 의해 액츄에이터(Actuator) 연결된다. 따라서  액츄에이터에 의해  플래터의 특정 위치를 찾아갈  있게 되며, 헤드가  플래터의 동일한 트랙을 돌며 움직이므로 실린더의 개념이 생겨난 것이다.

 

[하드디스크 인터페이스]

- 하드디스크의 인터페이스로 사용하는 규격은 크게 2가지가 있는데 ATA또는 IDE라고 불리며, 다른 하나는 SCIS라고 불린다.

- 일반적으로 우리가 쓰는 것은 ATA이며 IDE 혼용해서 이야기하는데 ATA라고 하는게 좋을 것이다.

- IDE(Integrated Drive Electronics) 인터페이스를 만든 회사 이름이며, ATA(Advanced Technology Attachment) IDE에서 만든  인터페이스가 ANSI 의해 표준으로 채택되고 나서 표준에 붙은 이름이다.

- ATA HDD 접근하기 위해 만들어진 표준이다. 이전에는 회사들이 각각 다른 인터페이스를 가져 호환이 되지 않았는데 이를 해결하기 위해 표준으로 채택하였다.

- ATA 가격이 저렴하고 인터페이스 제어가 편리한 부분이 많아 대중적으로 사용하게 되었고, SCSI 경우 ATA방식에 비해 빠르고 확장성이 좋지만 비싼편이다. ATA 방식이 SCSI 비해 저렴한 이유는 외부 컨트롤러를 따로 두지 않아도 되고, 인터페이싱을 하는 프로토콜이 SCSI 프로토콜보다 단순하기 때문이다.

- ATA 단점은 SCSI 비해 CPU 점유율이 높다는 건데 DMA 쓰게 되면 이문제도 많이 해결 하게 된다.

- PIO(Programmed I/O) 모드를 쓴다면 CPU점유율이 높은 편이다.

- ATA버전에 대한 용약이다.

버전

설명

ATA-1

최초의 규격 PIO 0, 1, 2 지원, swDMA 0, 1, 2 지원, mwDMA 0 지원

ATA-2

LBA 모 I 지원. PIO 3, 4 및 mwDMA 1, 2 추가

ATA-3

S.M .A.R.T 기능추가

ATAPI-4

ATAPI 규격과 통합 UDMA 0 1. 2. 3 추가

ATAPI -5

UDMA4(Ultra DMA 66) 추가

ATAPI -6

UDMA5(Ultra DMA 100) 추가

ATAPI -7

UDMA6(Ultra DMA 133) 추가 S-ATA 규격 포함

ATAPI-8 버전은 S-ATA 관련된 기술로 생략 하도록 한다.

    PIO : Programmed Input/Output 모드

    swDMA(single word DMA) : 섹터를  번에 1 밖에 전송하지 못하는 모드

    mwDMA(multi word DMA) : 섹터를 한번에 여러 개씩 전송할  있는 모드

    S.M.A.R.T : 보안과 하드디스크 자가 검증에 관련된 기술

    ATAPI(Advanced Technology Attachment Packet Interface) : CD-ROM 같은 장치를 연결   있음.

    UDMA(Ultra DMA) : 특수한 UDMA 컨트롤러를 통해 더욱 빠른 속도로 전송하는 모드

    S-ATA(Serial ATA) : Serial 동작하는 새로운 인터페이스 방식

 

전송모드 속도

전송모드

사이를 타임(ns)

최대 전송 속도(MB/Sec)

PIO 0  

600ns

3.3MB

PIO 1  

383ns

5.2MB

PIO 2

240ns

8.3MB

PIO 3  

180ns

11.1MB

PIO 4  

120ns

16.7MB

mwDMA 0  

480ns

4.2MB

mwDMA 1  

150ns

13.3MB

mwDMA 2  

120ns

16.7MB

UDMA 0  

240ns

16.7MB

UDMA 1  

160ns

25MB

UDMA 2  

120ns

33.3MB

UDMA 3  

90ns

44.4MB

UDMA4  

60ns

66.7MB

UDMA 5  

40ns

100MB

UDMA 6

30ns

133.3MB

- 소개한 모드는 전부 Parallel ATA로도 가능한 모드이며, S-ATA 사용하면 더욱 빠르게 전송할  있다. 하지만 임베디드 장비에서 하드디스크를 사용하는 경우에는 일반적으로 mwDMA 모드나 PIO 모드를 기장 많이 시용하게 된다. UDMA 모드가 빠르기는 하지만 전용 UDMA 컨트롤러가 없으면 사용할  없기 때문이다.

-  표에서 설명한 모드들은 하드디스크의 물리적인 속도를 의미하는 것이 아니라 하드디스크와 CPU간의 인터페이스 속도를 의미한다. 즉, CPU 하드디스크간의 인터페이스 속도가 아무리 빨라져도 실제로 하드디스크에 데이터가 기록되는 시간이 길어지면 별로 의미가 없다.

- 하드디스크는 내부에 원형의 플래터가 있고 이것이 보통 5400rpm에서 7200rpm으로 회전하며, (Arm) 끝에 달린 헤드가 플래터 위에서 왔다 갔다 하면서 데이터를 쓰거나 읽게 된다. 이렇게 헤드가 움직이면서 플래터에 접근  쓰기를 하는 속도는 그다지 빠르지 않다. 아무리 인터페이스 속도가 133MB /sec 올라간다고 해도 내부에서 하드디스크는 속도가 받쳐주지 못하면 병목현상 때문에 성능이 많이 좋아지지 않는다.

- 때문에 5400rpmUDMA -6보다는 7200rpm  UDMA-5  성능이 좋을 수도 있다.

- 이러한 문제를 해결하기 위해 하드디스크 내부에 캐시 ιache) 용량을 높이는 방법을 시용한다. 그러면 성능이 많이 향상된다.

 

[ATA 레지스터]

- ATA 레지스터들은 같은 어드레스의 레지스터라고 할지라도  레지스터를 읽느냐 쓰느냐에 따라 기능이 달라진다. 이때 코딩할  실수를 저지를  있는데, 예를 들어 레지스터  데이터 레지스터에 특정한 값을   값이 제대로 들어갔는지 확인하기 위해 데이터 레지스터를 읽어보는 코드를 짠다면 제대로 작동하지 않을 것이다.

 

- [컨트롤 블록 어드레스]

어드레스

기능

/CSO 

/CS1

AO

A1

A2

READ

WRITE

0

1

1

1

0

Alternated status

Device Control

0

1

1

1

1

Drive Address

사용  

 

- [커맨드 블록 레지스터]

어드레스

기능

/CSO 

/CS1

AO

A1

A2

READ

WRITE

1

0

0

0

0

Data

Data

1

0

0

0

1

Error

Feature

1

0

0

1

0

Sector Count

Sector Count

1

0

0

1

1

Sector Number(LBA bit 0-7)

Sector Number(LBA bit 0-7)

1

0

1

0

0

Cylinder Low(LBA bit 8-15)

Cylinder Low(LBA bit 8-15)

1

0

1

0

1

Cylinder High(LBA bit 16-23)

Cylinder High(LBA bit 16-23)

1

0

1

1

0

Drive/Head(LBA bit 24-27)

Drive/Head(LBA bit 24-27)

1

0

1

1

1

Status

Command

- 주로 커맨드 블록 레지스터를 이용하여 하드디스크를 제어하게 되는데 몇몇 레지스터들을 'Sector Number(LBA bit 0-7)' 부분은 이것은 CHS방식이냐 LBA방식이냐 따라서 같은 레지스터를 다르게 사용하기 때문이다.

- CHS 모드를 사용한다면  레지스터는 Sector Number 적는 레지스터가 되고, LBA HDD 동작시키면 LBA Address 0~7bit 적는 레지스터가 된다. 레지스터들과 커맨드의 자세한 내용은 www.t13.org 기술문서를 참고하도록 하자.

 

[CHS Address LBA Address]

CHS모드

- CHS모드는 각각 실린더(Cylinder), 헤드(Head), 섹터(Sector) 나타내며 여기서 말하는 실린더는 트랙의 수직적 요소를 합한 것이 아닌 트랙 자체의 개념과 같다고   있다. CHS 나타내는 주소는 실린더와 헤드는 0부터, 섹터는 1부터 시작한다.

- 디스크의 주소는 물리적인 CHS주소로 나타내어지며, 초기의 하드디스크를 접근하는 방법도 CHS 주소를 사용한다.

 

LBA모드

- ROM BIOS 이용한 CHS모드의 하드디스크의 접근은 현재 주로 사용되는 수십, 수백 GB 하드디스크를 이용할  없다. 최대 8GB 이용할  있을 것이지만, 이와 같은 제약을 극복하기 위해 물리적인 접근 방법이 아닌 논리적인 방식의 디스크 접근방법이 고안 되였다.

- LBA(Logical Block Area) CHS 모드와 같이 물리적인 특성에 따른 기본 단위를 따로 계산하는 것이 아니라 섹터들을 일렬로  늘어놓은 것과 같은 논리적인 개념이다. 따라서 논리적인 번지는 내부적으로 물리적인 위치로 계산될  있기 때문에 주소 지정 시에 CHS모등인지 LBA모드인지만 구분하여 지정해주면 사용 가능 하며 트랙과 헤드를 구분 지어 나타낼 필요가 없기 때문에 통상적으로 228 까지 표현할  있다.

- 하나의 블록은 섹터와 같으므로 '512Byte X 228 = 128GB' 까지 사용할  있게 된다.

- 최근에는 이도 모자라 48bit LBA까지 지원하고 있다.

- LBA모드는 하드디스크의 물리적 정보를 따로 파악하여 넣어줄 필요가 없다. CHS모드의 경우 파티션 테이블을 구성하더라도 각각 물리적인 정보를 파악하여 값을 따로 구하여 넣어야 하지만, LBA 모드라면 단순히 사용하는 섹터의 숫자만 입력해주는 것으로 충분하다.

 

CHS

LBA

섹터의 주소를 지정할  Cylinder, Head, Sector 주소를 정확히 적어 넣어서 지정하는 방식.

대용량화된 하드디스크에서는 쓰이지 않으며, 다만 ATA 레지스터 이름에서만 흔적을 찾을  있다.

하드디스크를 각각 512Byte 블록으로 나눈 다음 각각의 블록에 순차적으로 번호를 매겨 주소를 할당하는 방법.

120GB까지는 28bit 값을 가지고 제어 했으나 28bit 한계가 다다르게 되어 확장된 LBA 주소방식인 48bit LBA어드레스 방식을 사용

 

28bit 처리할  있는 최대용량?

1block 512byte 담당.

268,435,455(228) X 512 = 137,438,952,960  127GB

 

- LBA 방식에서 최소 단위는 1LBA(512Byte)이다. 하드디스크 Address 0~511번째 바이트는 LBA 0번에 해당된다.

하드디스크 Address 512~1023번째 바이트는LBA 1번에 해당된다.

만약 30번째 바이트를 0xFF라고 적고 싶을  30번째 1바이트만 0xFF 바꾸고 싶다  지라도 우선 0 LBA 512Byte 전부  읽고 30번째를 0xFF 수정한  다시 LBA 0번에 덮어 써야 한다.  1바이트를 읽어도 무조건 1LBA(512Byte) 단위로 행동 해야 한다.

 

5.저장장치 실습

Visual C++ 컴파일을 해보도록 하자.

아래 소스는 하나의 LBA 읽어서 화면에 값을 보여주는 예제이다.

특히 HDD_read() 함수는 앞으로 모든 예제에 쓰일 것이다.

 

Visual Studio 쓸때는 우클릭  관리자 권한으로 실행을 눌러서 켜자

 

그리고 프로젝트 우클릭  속성  일반탭의

문자집합 : 멀티바이트 문자 집합 사용으로 셋팅을 하도록 하자.

 

 

소스 중에서 필요한 부분만 입력했습니다.

진하게  부분은 살짝 수정한 부분 입니다.

#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

 

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

void HexDump (U8 *addr, U32 len);

 

int main(void) {

    U8 dumpData[512];

 

    HDD_read(0, 0, 1, dumpData);

    HexDump( dumpData, 512);

    return 0;

}

 

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;

     //WinAPI를 이용한 저장장치의 섹터 하나에 데이터를 읽는 함수

    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;

}

 

void HexDump (U8 *addr, U32 len){  //버퍼에 들어 있는 값을 출력

    U8        *s=addr, *endPtr=(U8*)((U32)addr+len);

    U32        i, remainder=len%16;

        

    printf("\n Offset Hex Value Ascii value\n");

    

    // print out 16 byte blocks.

    while (s+16<=endPtr){

        

        // offset 출력.

        printf("0x%08lx ", (long)(s-addr));

        

        // 16 bytes 단위로 내용 출력.

        for (i=0; i<16; i++){

            printf("%02x ", s[i]);

        }

        printf(" ");

        

        for (i=0; i<16; i++){

            if (s[i]>=32 && s[i]<=125)printf("%c", s[i]);

            else printf(".");

        }

        s += 16;

        printf("\n");

    }

    

    // Print out remainder.

    if (remainder){

        

        // offset 출력.

        printf("0x%08lx ", (long)(s-addr));

        

        // 16 bytes 단위로 출력하고 남은 것 출력.

        for (i=0; i<remainder; i++){

            printf("%02x ", s[i]);

        }

        for (i=0; i<(16-remainder); i++){

            printf(" ");

        }

 

        printf(" ");

        for (i=0; i<remainder; i++){

            if (s[i]>=32 && s[i]<=125) printf("%c", s[i]);

            else    printf(".");

        }

        for (i=0; i<(16-remainder); i++){

            printf(" ");

        }

        printf("\n");

    }

    return;

}    // HexDump.

 

실행 화면

 

+ Recent posts