FAT32 기반의 USB 복구프로그램 (ver. 01) by hyd3(hyde0401@gmail.com)
Contents 1. 개요... 2 1.1. FAT 32... 2 1.2. 수행일정 / 수행인원... 2 2. FAT 32 이론및분석실습... 3 2.1. 실습환경... 3 2.2. MBR... 3 2.3. FAT 32 파일시스템의구조... 7 2.4. FAT 32 세부구조 Boot Record... 7 2.5. FAT 32 세부구조 Reserved Area... 12 2.6. FAT 32 세부구조 FAT Area... 14 2.7. FAT 32 세부구조 Data Area... 15 2.8. FAT 32 세부구조 LFNS... 20 3. 파일생성 / 복구의이해... 23 3.1. 파일생성... 23 3.2. 파일복구... 25 4. FAT32 기반의 USB 복구프로그램제작... 27 4.1. Source Code... 27 4.2. Excuting Program... 45 4.3. 향후계획... 47 1 페이지
1. 개요 1.1. FAT 32 File Allocation Table 이라는파일시스템으로 Windows 기반의주로작은용량의 HDD 에서 사용되었던간단한파일시스템이다. 하지만현재는 NTFS 등이등장하면서 FAT 파일시스템은 USB 등의이동식저장장치등과같은 용량이작은장치의파일시스템으로사용이되고있다. 본프로젝트에서는 USB 복구프로그램제작과정을통해 FAT 32 FileSystem 에대한이해도를 높이고자한다. 1.2. 수행일정 / 수행인원 본모의해킹은 2014 년 8 월 11 일부터 ~ 2014 년 8 월 27 일까지진행이되며, 총 1M/M 가 투입됩니다. Task 별자세한일정은아래표와같습니다. 8 월 11 일 ( 월 ) 8 월 12 일 ( 화 ) 8 월 13 일 ( 수 ) 8 월 14 일 ( 목 ) 8 월 15 일 ( 금 ) 파일시스템이론정리 파일시스템이론정리 FAT 16 이론정리 FAT 16 기반의파일시스템분석 FAT 16 기반의파일시스템분석 8 월 18 일 ( 월 ) 8 월 19 일 ( 화 ) 8 월 20 일 ( 수 ) 8 월 21 일 ( 목 ) 8 월 22 일 ( 금 ) FAT 32 이론정리 FAT 32 기반의파일시스템분석 FAT 32 기반의파일시스템분석 FAT 32 기반의파일시스템분석 get_partition get_bpb_info show_dir show_del_dir print_longname 구현 8 월 25 일 ( 월 ) 8 월 26 일 ( 화 ) 8 월 27 일 ( 수 ) rec_file 구현 rec_dir 구현최종보고서작성 [ 표 1-1] 제작일정 2 페이지
2. FAT 32 이론및분석실습 2.1. 실습환경 실습환경은 [ 표 2-1] 과같으며실제실습및프로그램구현은 Guest PC 에서진행되었다. Host PC Windows 7 64 bit Guest PC Windows xp sp3 Hex Editor HxD Coding Tool Visual Stdio 2008 Version [ 표 2-1] 실습환경 2.2. MBR 2.2.1. MBR 이란? 파티션이여러개인다중파티션의경우각파티션의첫번째섹터에존재하는 BR 만으로는 어떤파티션을부팅으로사용할지알수없다. 이때문에 BR(Boot Record) 중에서메인격인 MBR(Master Boot Record) 를두어이역할을제공하였다. [ 그림 2-1] MBR 의구조 - Boot Flag : 해당파티션의부팅가능여부 (0x80 : 부팅가능파티션, 0x00 : 부팅불가능파티션 ) 3 페이지
- CHS Starting Address : CHS 방식의시작주소 - Partition Type : 해당파티션의종류를의미 (0x0B : CHS 모드의 FAT32, 0x0C : LBA 모드의 FAT32) - CHS Ending Address : CHS 방식의종료주소 - LBA Start Address : LBA 방식의시작주소 - Size in Sector : LBA 의총개수 각어플리케이션에따라파티션이 CHS 주소를사용할지 LBA 주소를사용할지에대해 결정되기때문에분석시에는우선 Partition Type 를확인하고그에맞는주소값을확인해야한다. 2.2.2. MBR 분석 기존에알아본정보를토대로실제 HDD 의 MBR 을분석해보도록하겠다. [ 그림 2-2] HDD 파티션할당확인 [ 그림 2-2] 와같이현재 HDD 의분석하고자하는파티션을확인한다. [ 그림 2-3] HxD 를활용한 HDD 접근 [ 그림 2-3] 과같이 HxD 를활용하여 [ 그림 2-2] 에서확인한 HDD 에접근한다. 그후 MBR 은각저장장치의 0 번섹터에온다는사실을염두에두고분석을시작해야한다. 4 페이지
[ 그림 2-4] HDD 02 s MBR 전체구조확인 HDD 02 의 MBR 전체구조는 [ 그림 2-4] 와같이 Boot Code 와 4 개의 Partiton Table, 마지막으로 signature 로이루어져있다. 하지만현재 Partition 01 과 Partition 02 만이사용하고있다는것을알수있으며이제부터이두개의파티션테이블을분석해보도록하겠다. Partition 01 : 00 01 01 00 0B FE BF 7C 3F 00 00 00 FE 25 9C 00 - Boot Flag : 00, 부팅불가파티션 - CHS Starting Address : 0x00, 0x01, 0x01 - Partition Type : 0x0B, CHS 방식의 FAT32 - CHS Ending Address : 0x7C, 0xBF, 0xFE Partition 02 : 00 00 81 7D 0C FE FF FF 3D 26 9C 00 9C BF A3 00 - Boot Flag : 0x00, 부팅불가파티션 - Partition : 0x0C, LBA 방식의 FAT32 - LBA Starting Address : 0x009C263D, 10,233,405 5 페이지
- Size Of Sector : 0x00A3BF9C, 10,731,420 => LBA 블록 1 개 : 512Byte, 파티션총용량 : 10731420 Sector * 512Byte = 5,494,487,040Byte 마지막으로실제부팅이가능한파티션에대한분석을해보고 MBR 을정리하고자한다. [ 그림 2-3] 에서이번엔하드디스크 1 을선택한다. [ 그림 2-5] HDD 1 분석 하드디스크 1 을 HxD 를통해열면 [ 그림 2-5] 와같으며 HDD 1 내에서도 Partition 1 부분만 따로떼어분석해보도록하겠다. Partition 1 : 80 01 01 00 07 FE FF FF 3F 00 00 00 0B AC FF 09 - Boot Flag : 0x80, 부팅가능 - Partition Type : 0x07, NTFS - CHS Starting Address : 0x00 0x01 0x01 - CHS Ending Address : 0xFF 0xFF 0xFE - LBA Starting Address : 0x0000003F, 63 - Size in Sector : 0x09FFAC0B, 167,750,667 => 파티션총용량 : 167,750,667 * 512 Byte = 85,888,341,504 Byte = 79GB 6 페이지
2.3. FAT 32 파일시스템의구조 [ 그림 2-6] FAT 32 파일시스템의구조 - Boot Record : Boot Record 는볼륨의첫번째섹터이자 Reserved Area 의첫번째섹터이다. 이영역에는부팅하기위한 Boot Code 와함께 FAT 파일시스템의값들이저장되어있으며 BIOS Parameter Block (BPB) 라고도한다. - Reserved Area : 나중을위해비워둔예약된공간이다. - FAT #1 / FAT #2 : 클러스터를관리하기위한테이블이모여있는공간이다. 이공간은 HDD 내의파일들과연결된클러스터에대한정보들이있으므로만약손상이되면해당파일을사용할수없다. 이때문에 FAT #1 과동일한정보를 FAT #2 에백업하게된다. - Root Directory : Data Area 내의아무곳에나존재해도관계없으며그위치를 Boot Record 에기록한다. 하지만 FAT 32 이전의파일시스템즉 FAT 16 등에는 FAT 영역바로다음에 Root Directory 가존재하였으므로일반적으로는 FAT 32 에서역시 FAT 영역바로뒤에둔다. - Data Area : 파일또는디렉토리가저장되어있는영역으로이전영역과의차이는이전영역의경우 Sector 단위로읽기쓰기를하지만 Data Area 의경우 Cluster 단위로읽기와쓰기를진행한다. 2.4. FAT 32 세부구조 BOOT RECORD 2.4.1. Boot Record 이론정리 [ 그림 2-7] Boot Record 의구조 7 페이지
FAT 32 의구조는 [ 그림 2-7] 과같으며 36 Bytes 는 FAT 16 과공통적인부분이고 FAT 32 에만포함되는영역은그이후에존재한다. - Jump Boot Code : Boot Code 로점프하기위한코드 - OEM Name : OEM 회사를나타내는문자열이들어있음 - Bytes per Sector : 한개섹터를구성하는바이트수 - Sector per Cluster : 한개클러스터를구성하는섹터수 => Sector Per Cluster * Bytes Per Sector = Cluster 의크기 - Reserved Sector Count : [ 그림 3-1] 의 Reserved Area 의섹터개수 - Number of FATs : 해당볼륨에존재하는 FAT 영역의개수 - Root Directory Entry Count : Root Directory 내에몇개의파일 / 디렉토리를수용할지에대해기록하는항목으로반드시 Bytes Per Sector 의짝배수로구성이되어야한다. 하지만 FAT 32 에서는반드시 0 으로채워져야한다. - Total Sector 16 : FAT 16 에해당되는항목으로 FAT 32 에서는 0 으로채워진다. - Media : 해당볼륨에어떤미디어가저장되어있는지기록되며플로피디스크를제외한모든 장치의경우 0xF8 로기록이된다. - FAT Size 16 : FAT 영역의섹터수를저장하는부분으로 FAT 32 에서는 0 으로채워진다. - Sector Per Track : Track 당 Sector 의개수를의미하는데 Window 계열에서는더이상이값을참조하지않는다. - Number Of Heads : 해당저장장치의헤더수를의미하며 Window 계열에서는더이상이값을참조하지않는다. - Hidden Sector : 해당볼륨앞의숨겨진섹터수를의미하며 Window 계열에서는더이상이값을참조하지않는다. - Total Sector 32 : 해당볼륨상의총섹터수를의미한다. - FAT Size 32 : FAT 영역의섹터수를의미하는데이수는 FAT #1 과 FAT #2 의합산이아닌한 개의 FAT 영역의섹터수를의미한다. - Ext Flags : FAT 테이블의사용여부등의여러가지설정값을기록한다. 8 페이지
- File System Version : FAT32 의버전정보로상위 1Byte 는주, 하위 1Byte 는부버전을의미한다. 하지만실제로는특정값이적히지않고 0x00 으로기록된다. - Root Directory Cluster : FAT32 는이전버전들과는달리 Root Directory 가 Data Area 내아무위치에나와도상관없기때문에그시작위치를 Root Directory Cluster 에기록한다. - File System Information : File System 구조체의위치가기록되나. - Boot Record Backup Sector : Boot Record 를백업한위치를기록하는것으로일반적으로는 6 번 섹터를이용한다. 만약이위치에 0 이기록되어있다면백업을하지않았음을의미한다. - Reserved : 예약된영역으로항상 0 으로채워진다. - Dirve Number : Window 계열에서는더이상참조하지않는다. - Reserved1 : Windows NT 계열에서사용하려고만든예약된영역이며 0 으로채워져있다. - Boot Signature : 확장부트서명으로 0x29 라는값이들어간다.( 이후에 3 가지항목이더 존재함을의미 ). - Volume ID : 볼륨의시리얼번호 - Volume Label : 볼륨레이블을적어준다. - File System Type : 항상 FAT32 로적혀있다. 이러한내용을토대로실제 FAT 32 를사용하는 Partition 의 Boot Record 를분석해보도록하겠다. 2.4.2. Boot Record 분석실습 [ 그림 2-8] Partition 2 의 Boot Record Area 찾아가기 9 페이지
Boot Record 영역의분석을위해서는이전에분석했던 MBR 영역에서 Partition 2 의 Boot record 영역을찾아가야한다. 일반적으로 Boot Record 의경우예약된영역의첫번째섹터, 해당볼륨의첫번째섹터에존재하기때문에이를이용하면해당파티션의첫번째섹터를찾아간다고생각하면된다. [MBR - Partition Table - LBA Starting Address] -> 10진수변환 -> 이동위의방법대로 MBR 의 Partition Table 내에존재하는 LBA Startin Address 를찾아보니 0x009C263D 임을확인할수있다. 이를 10 진수로변환하면 10233405 이며이단위는섹터이므로 HxD 에서 10233405 번째섹터를찾아가면 [ 그림 2-8] 과같이 Partition 2 의 Boot Record 를확인할수있다. [ 그림 2-9] Partition 2 의 Boot Record Partition 2 의 BR Area 영역은 [ 그림 2-9] 와같은데이를상세히분석해보도록하겠다. EB 58 90 4D 53 44 4F 53 35 2E 30 00 02 08 20 00 02 00 00 00 00 F8 00 00 3F 00 FF 00 3D 26 9C 00 9C BF A3 00 DC 28 00 00 00 00 00 00 02 00 00 00 01 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 29 8E D8 6B F8 4E 4F 20 4E 41 4D 45 20 20 20 20 46 41 54 33 32 20 20 20 - Jump Boot Code : 0x9058EB - OEM Name : 0x302E35534F44534D - Bytes per Sector : 0x0200, 512Byte - Sector per Cluster : 0x08, 1 Cluster == 8 Sector, Cluster Size : 512 * 8 = 4096 Byte 10 페이지
- Reserved Sector Count : 0x0020, 32 Sector - Number of FATs : 0x02, FAT #1, FAT #2 2 개 FAT 영역보유 - Root Directory Entry Count : 0x0000 - Total Sector 16 : 0x0000 - Media : 0xF8, 고정식디스크 - FAT Size 16 : 0x0000 - Sector Per Track : 0x003F - Number Of Heads : 0x00FF - Hidden Sector : 0x009C263D - Total Sector 32 : 0x00A3BF9C, 10731420 Sector - FAT Size 32 : 0x000028DC, 10460 Sector - Ext Flags : 0x0000 - File System Version : 0x0000 - Root Directory Cluster : 0x00000002, Cluster 2 - File System Information : 0x0001, Sector 1 - Boot Record Backup Sector : 0x0006, Sector 6 - Reserved : 0x000000000000000000000000 - Dirve Number : 0x80 - Reserved1 : 0x00 - Boot Signature : 0x29 - Volume ID : 0x2020454D - Volume Label : 0x2020414E204F4EF86BD88E - File System Type : 0x2020203233544146, FAT32 11 페이지
2.5. FAT 32 세부구조 RESERVED AREA 2.5.1. Reserved Area 이론 예약된영역의첫번째섹터는 Boot Record 로바로이전에분석한내용이다. 그리고나머지 영역은비어야하지만일반적으로는 Boot Record 에기록된것과같이 Sector 1 와 Sector 6 에는 File System Information 구조체와 Boot Record Backup 이각각기록되어있다. [ 그림 2-10] Reserved Area(FS Info 구조체 ) 구조 - Lead Signature : FSInfo 구조체가기록된섹터임을알려준다. 0x41615252 로고정되어있다. - Reserved 1 : 예약영역으로해당섹터의대부분을차지하고있으며 (479Byte) 전부 0 으로채워져있다. - Struct Signature : 실제 FSInfo 구조체의시작을알려주는항목으로 0x61417272 로고정되어있다. - Free Cluster Count : 볼륨상에존재하는빈클러스터의수 - Next Free Cluster : 가장마지막에할당된클러스터의값이저장되며이바로뒤에오는 클러스터는비어있는클러스터라고할수있다. - Reserved 2 : 이역시예약된영역이며 0 으로채워진다. - Trail Signature : FS Info 구조체섹터의끝을알려주며 0xAA550000 로고정되어있다. 이내용을토대로실제 Partition 2 의 Reserved Area 를분석해보도록하겠다. 12 페이지
2.5.2. Reserved Area 분석 [ 그림 2-11] Partition 2 의 Reserved Area Boot Record 에서 FS Info 구조체의위치는 Sector 1 로나와있었다. 하지만이는섹터번호가 0 부터시작하기때문에첫번째섹터가아니라해당파티션의두번째 섹터를의미하게된다. 위에서확인한것과같이섹터의시작은 0x41615252 로채워져있다. 00 00 00 00 72 72 41 61 B7 6D 14 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA - Struct Signature : 0x61417272 - Free Cluster Count : 0x00146DB7, 1338807 개클러스터가비어있다. - Next Free Cluster : 0x00000003, Cluster 3 번이가장마지막에채워진클러스터이다. - Trail Signature : 0xAA550000 13 페이지
2.6. FAT 32 세부구조 FAT AREA 2.6.1. FAT Area 이론 FAT 영역은 FAT 파일시스템에서주요정보를담고있다. 그중에는파일 / 디렉토리의할당에 대한정보를클러스터단위로관리한다. [ 그림 2-12] FAT Area 의구조 [ 그림 2-12] 는 FAT Area 의구조로최소 8Byte 를제외하고는전부클러스터단위로파일을관리하는것을확인할수있다. Cluster 2 부터시작하는이유는이전에 Cluster 0 과 Cluster 1 는이미다른용도로사용되었기때문인데이는차후에알아보도록하겠다. 이때각클러스터를관리하는 4Byte 단위의항목들을 FAT Entry 라고하며각각은다음클러스터번호가기록된다. 이를분석하기에앞서알아둬야할점은 FAT Area 는단일링크드리스트방식으로데이터를저장했다는것인데이를이용하여각클러스터는한개클러스터로해당파일 / 디렉토리를저장할수없는경우에한개의클러스터를더할당하고이두번째클러스터의번호를첫번째클러스터에기록하게된다. 그리고마지막클러스터에는 0x0FFFFFFF 를기록하여마지막을표시한다. 단순설명으로는이해가되지않기때문에예제를들어이해를돕도록하겠다. FAT Area 영역의경우 Reserved Area 바로다음에오게되어있는데 Partition 2 의 Reserved Area 크기는 32 Sector 로이루어져있음을이전에분석해주었기때문에 Reserved Area 의시작위치에서 32 Sector 만큼이동하면 [ 그림 2-13] 과같이 FAT Area 로이동하게된다. 14 페이지
2.6.2. FAT Area 분석 [ 그림 2-13] Partition 2 의 FAT Area [ 그림 2-13] 은 FAT Entry 의 Cluster 단위파일 / 디렉토리관리에대한예제이다. 한개클러스터 영역으로저장이가능한클러스터 2 와클러스터 3 과는달리클러스터 4 에저장된파일의경우 용량이큰지다음클러스터번호가적혀있다. [ 그림 2-14] Partition 2 의 FAT Entry [ 그림 2-13] 을통해 Cluster 4 에서시작된파일은 Cluster 16 까지연결되는것을확인할수있으며이를그림으로그려보면 [ 그림 2-14] 와같다. Cluster 16 에서는 0x0FFFFFFF 을기록하여파일의끝을알렸고이를 EOC(End Of Cluster) 라고한다. 2.7. FAT 32 세부구조 DATA AREA 2.7.1. Data Area 이론 Data Area 는실제데이터들이저장되는영역을말한다. 데이터는클러스터단위로저장이되는데데이터가한개클러스터보다크게되면클러스터를더할당하여저장하게된다. 이경우만약저장후클러스터의공간이남아있다하더라도이미사용한클러스터이기때문에다른파일 / 디렉토리의데이터를저장할수없다. 실제데이터를저장할때는파일이냐디렉토리냐에따라저장방식에차이가있는데만약파일이라면클러스터에파일의내용이직접적으로저장이되며 15 페이지
디렉토리라면 Directory Entry 라는구조체형식으로저장이된다. [ 그림 2-15] Directory Entry 구조 - Name : 파일 / 디렉토리의이름, 대문자로최대 8 자까지넣을수있으며남는공간은 0x20 으로채워진다. 단만약해당파일 / 디렉토리가지워진경우에는최고상위 bit 가 0xE5 로채워진다. - Extender : 확장자, 대문자로최대 3 자리까지입력이가능하며이역시남는공간은 0x20 으로채워지는데디렉토리의경우에도이공간이 0x20 으로채워진다. - Attribute : 해당 Directory Entry 의용도를기록하는항목으로 System File 은 0x04 로, 서브디렉토리의경우 0x10, 일반파일은 0x20, LFN 이적용되었다면 0xF0 로채워진다. - NT Resource : Windows NT 의예약된공간으로항상 0 으로채워져있다. - Create Time Tenth : 파일이생성된시간을 1/10 초단위로기록하는항목. - Create Time : 파일생성시간으로상위 5bit 는초, 다음 6bit 는분, 마지막 5bit 는시를의미한다. 다른값은일반적으로식별하는시간이맞으나초의경우값이 1 증가할때마다 2 초씩증가한다. - Create Date : 생성날짜로상위 5bit 는일, 다음 4bit 는월, 마지막 7bit 는년을의미한다. 단년도의경우 1980 년으로부터의오프셋인데만약 11 이라면 1980+11=1991 년을의미한다. - Last Access Date : 가장최근의읽기 / 쓰기를한날자를의미한다. - First Cluster High 2Byte : 파일의첫번째클러스터번호의상위 2Byte 를말한다. - Write Time : 최근파일수정시간을말하며파일생성역시쓰기로간주하기때문에파일최소 생성시에는이 Write Time 과 Create Time 이일치한다. - Write Date : 최근수정일자로파일최초생성시에는 Create Date 와일치한다. - First Cluster Low 2Byte : 파일의첫번째클러스터의하위 2Byte 를말한다. - File Size : 파일의크기를의미하며단위는 Byte 이다. 만약디렉토리라면이항목은 0 으로 채워진다. 16 페이지
2.7.2. Data Area 분석 [ 그림 2-16] Partition 2 Directory Entry 분석예제 [ 그림 2-16] 과같이 F:\ 로명명된 Partition 2 의 Root Directory 에는 dirtest 라는디렉토리가 있으며그내부에는 test04.txt 라는파일을갖고있다. 지금부터 Root Directory Entry 부터이 test04.txt 라는파일을찾아가보도록하겠다. [ 그림 2-17] Partition 2 Root Directory Entry [ 그림 2-17] 은 Partition 2 의 Root Directory Entry 로최상위디렉토리를의미하는데이곳에 있는 Directory 중 Attribute 항목이디렉토리로, 즉 0x10 으로표기된항목을찾으면가장하위에 있는것을확인할수있다. 이를상세분석해보도록하자. 44 49 52 54 45 53 54 20 20 20 20 10 08 2A 17 89 03 45 03 45 00 00 18 89 03 45 07 00 00 00 00 00 17 페이지
- Name : 0x4449525445535420, 파일명 DIRTEST - Extender : 0x202020, Directory - Attribute : 0x10, Directory - NT Resource : 0x08 - Create Time Tenth : 0x2A - Create Time : 0x8917, 10001 001000 10111 => 17 시 8 분 46 초 - Create Date : 0x4503, 0100010 1000 00011 => 2014 년 8 월 3 일 - Last Access Date : 0x4503, 2014 년 8 월 3 일 - First Cluster High 2 Byte : 0x0000 - Write Time : 0x8918, 10001 001000 11000 => 17 시 8 분 48 초 - Write Date : 0x4503, 2014 년 8 월 3 일 - First Cluster Low 2Byte : 0x0007 => High + Low = 0x00000007 => Cluster 7 - File Size : 0x00000000, Directory 분석중 Dirtest Directory 의위치는 7 번클러스터라는것을알게되었으므로해당클러스터로 이동한다. 한개클러스터는 8 Sector 이므로 (7-2) * 8 = 40 Sector 만큼이동한다. 현재 2 번클러스터라는것을기억하고계산해야한다. [ 그림 2-18] DIRTEST Directory 의 DirectoryEntry "DIRTEST" Directory 의클러스터를찾아가면 [ 그림 2-18] 과같은데이곳에서도 Attribute 항목에 0x20, 즉일반파일을찾아서확인해보면 TEST4.txt 의 Directory Entry 를찾을수있다. 18 페이지
그후 TEST4.txt 의데이터위치를확인하려면이 Directory Entry 를상세분석해야한다. 54 45 53 54 30 34 20 20 54 58 54 20 18 72 1C 89 03 45 03 45 00 00 22 89 03 45 0F 00 06 00 00 00 - name : 0x5445535430342020, 파일명 TEST4 - Extender : 0x545854, 확장자 TXT - Attribute : 0x20, Archive File - Create Time Tenth : 0x7218 - Create Time : 0x891C, 10001 001000 11100 => 17 시 8 분 56 초 - Create Date : 0x4503, 2014 년 8 월 3 일 - Last Access Date : 0x4503, 2014 년 8 월 3 일 - First Cluster High 2Byte : 0x0000 - Write Time : 0x8922, 10001 001001 00010 => 17 시 9 분 4 초 - Write Date : 0x4503, 2014 년 8 월 3 일 - First Cluster Low 2Byte : 0x000F => High + Low = 0x0000000F => Cluster 15 - File Size : 0x00000006 분석결과로 TEST4.TXT 의데이터위치는 15 번클러스터로확인되었으므로 (15-2) * 8 = 104 Sector 만큼이동해야한다. [ 그림 2-19] TEST04.TXT 내용확인 해당섹터로이동하여파일의내용을확인해보면실제파일의내용과일치하는것을확인할수 있다. 19 페이지
2.8. FAT 32 세부구조 LFNS 2.8.1. LFNs(Long File Name Entrys) 이론 Long File Name Entrys 란기존의파일작명방식은이름 8 자확장자 3 자의한계를보완하기위해생겨난작명방법이다. 이 LFNs 방식은 UTF-16 인코딩을사용하여다국어지원이가능해졌으며최대 255 자까지작명이가능하게되었다. [ 그림 2-20] LFNs 구조 - Order : LFNs 의순서를기록하는항목으로가장처음은 0x01 로시작되며가장마지막은 0x40 과 OR 연산을한값으로넣는다. 자세한사항은실습에서설명하도록하겠다. - Name 1 : 1~5 번째문자열 - Attribute : 항상 0x0F 으로고정되어있다. - Type : 일반적으로 0x00 으로채워진다. - Checksum : 해당 LFNs 과대응되는 Short Directory Entry 의 Checksum 을저장한다. - Name 2 : 6~11 번째문자열 - First Cluster Low : 반드시 0 이들어간다. - Name 3 : 12~13 번째문자열 이를토대로실제 Partition 2 의 LFNs 를사용하는파일을분석해보도록하겠다. 20 페이지
2.8.2. LFNs(Long File Name Entrys) 분석 [ 그림 2-21] LFNs 실습을위한파일명 / 내용확인 [ 그림 2-21] 과같이 F:\LFN Test\thislfntestfile 이라는파일을분석하여 LFNs 에대해확인하고자한다. 파일명은 thisislfntestfile 이며확장자를빼고도 16 개문자로이루어져있기때문에기존의 Short Directory Entry 로는저장할수없다. [ 그림 2-22] Partition 2 LFNs 확인 Test File 의 Directory Entry 로이동해보면 THISIS~1TXT 로파일명과확장자가표현된것을 확인할수있는데그윗쪽을살펴보니 Attribute 가 0x0F 로되어있는 2 개의 Entry 가보인다. 이는해당 Test File 의 LFNs 임을알수있다. 42 66 00 69 00 6C 00 65 00 2E 00 0F 00 43 74 00 78 00 74 00 00 00 FF FF FF FF 00 00 FF FF FF FF 01 74 00 68 00 69 00 73 00 69 00 0F 00 43 73 00 6C 00 66 00 6E 00 74 00 65 00 00 00 73 00 74 00 21 페이지
LFNs 에대해서만상세히분석해보고자하는데우선 2 개의 Order 를확인해보면가장처음은 01 로그다음에는 42 로시작하는것을확인할수있다. 이는 0x40 0x02 의결과로해당 LFNs 가가장마지막 LFNs 임을의미한다. 그러므로 1 번 Entry 의 Name 1 부터 Name 3 까지읽고그다음 2 번 Entry 의 Name 1 부터 Name 3 까지읽으면 thisislfntestfiletxt 로정확히파일명과일치하는것을알수있다. 22 페이지
3. 파일생성 / 복구의이해 3.1. 파일생성 복구방식을이해하기위해서는우선적으로파일이어떤방식으로생성되는지에대해알고있어야하기때문에먼저이에대해이해하고넘어가고자한다. FAT 파일시스템은 FAT Table, Directory Entry, File 내용등을기록함으로써파일을생성하는데지금부터이방식을설명하며직접파일을생성해보도록하겠다. 3.1.1. FAT Table -> 빈클러스터확인. [ 그림 3-1] 파일의생성을위한빈클러스터번호확인 [ 그림 3-1] 과같이기존실습에서활용했던 Partition 1 의 FAT 영역으로이동하여빈클러스터 번호인 16 번클러스터를확인하였다. 3.1.2. Data Area -> Step 1 에서확인한빈클러스터에파일내용기록. [ 그림 3-2] 빈클러스터에파일내용기록 기존 Data Area 에서 16 번클러스터를찾아가야하므로 20047 + (16-2)*8 = 20159 Sector 가 나온다. 이를찾아가서원하는내용을기록하면 [ 그림 3-2] 와같다. 3.1.3. FAT Table -> Step 2 에서기록한파일크기에따라클러스터할당. [ 그림 3-3] Cluster 할당 23 페이지
[ 그림 3-3] 과같이 FAT Area 에서 16 번 FAT Entry 에파일이기록되었음을표시해야하는데해당파일의내용이한개클러스터의용량을넘지않기때문에 EOC 를의미하는 0x0FFFFFFF 을적어준다. 3.1.4. Root Directory Entry -> File 정보기록 [ 그림 3-4] Directory Entry 에생성파일정보기록 이제파일의정보를상위디렉토리의 Directory Entry 에기록해야하는데이때주의할점은다른 날짜와파일명은임의로기록해도되지만해당파일의클러스터번호는정확히적어주어야한다. 3.1.5. 파일생성결과확인 [ 그림 3-5] 파일생성결과확인 해당내용을저장한후재부팅하면 [ 그림 5-5] 와같이파일이생성되었고내용역시정확히입력 되어있는것을확인할수있다. 24 페이지
3.2. 파일복구 실제복구에앞서간단히순서를살펴보자면우선 Data Area 에서지워진 Directory Entry 에서지워지기전에해당파일이사용하던클러스터번호를확인한다. 그후 FAT Area 에확인한클러스터번호에맞는 FAT Entry 와연결시켜주고마지막으로 Directory Entry 의 NAME 의항목에서 0xE5 로되어있는부분을수정한다. 본문서에서는삭제된디렉토리를복구하고그안에들어있는파일까지복구하는식으로실습을진행하도록하겠다. 3.2.1. 삭제된파일확인 [ 그림 3-6] 삭제파일확인 우선타겟으로할삭제된 Directory 를찾아야하는데 [ 그림 3-6] 과같이 ATTEST 라는디렉토리가삭제된흔적이보인다. 이디렉토리를우선적으로복구하기위해서속성값을확인한결과 0x000B, 즉삭제되기전에 11 번클러스터를사용했다는것을알수있다. 3.2.2. FAT Entry 할당 [ 그림 3-7] FAT Area 에서의삭제파일 FAT Entry 확인 25 페이지
11 번클러스터에대한 FAT Entry 를확인하여만약사용하지않음으로되어있다면사용하는 클러스터사이즈에맞게설정해주도록하자 3.2.3. 지워진파일의 Signature 변경 (0xE5) [ 그림 3-8] FATTEST 디렉토리복구를위한속성값변경 [ 그림 3-6] 에서삭제된파일 / 디렉토리를의미하는 0xE5 로되어있던 NAME[0] 을 [ 그림 3-8] 과같이다른값으로변경시켜주고저장후재부팅해보도록하자. 3.2.4. Root Directory Entry 복구결과확인 [ 그림 3-9] FATTEST 디렉토리복구결과확인 재부팅을한후에확인해보니 [ 그림 3-9] 와같이디렉토리가복구된것을확인할수있다. 하지만현재디렉토리만복구되었을뿐내부의파일은아직복구되지않았기때문에 FATTEST 디렉토리내용이담긴 11 번클러스터로이동해서내용을확인하자. 3.2.5. Sub Directory Entry 복구 26 페이지
[ 그림 3-10] E:\FATTEST\ 내용확인 11 번클러스터위치 : 20047+(11-2)*8 FATTEST 디렉토리내의삭제된파일을보면 [ 그림 3-10] 과같은데이중에서마지막에있는 ECTEST.TXT 파일을복구해보도록하겠다. 22 번클러스터를사용하므로 [ 그림 3-7] 을통해 FAT Area 의 FAT Entry 를확인하고이역시 0xE5 를다른문자로대체한후재부팅을진행하자. 3.2.5. Sub Directory Entry 복구결과확인 [ 그림 3-11] RECTEST.TXT 파일복구결과확인 재부팅을하게되면 [ 그림 3-11] 과같이내용까지정상복구되었음을확인할수있다. 4. FAT32 기반의 USB 복구프로그램제작 4.1. SOURCE CODE #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 #define PARTITION_TBL_POS 0x1BE // MBR 영역에서 0~446 Byte 이후에실제 Partition Table이등장하므로따로기록 /* * ------------- 구조체설명 ----------------- * PARTITION : partition 의정보를담기위한구조체 * FAT32_BPB : BPB 의정보를담기위한구조체 27 페이지
* VolStruct : FAT32_BPB 에서필요한정보를추출하기위한구조체 * DitEntry : Directory Entry 의정보를저장하기위한구조체 * LongDirEntry : LFN의 Directory Entry를저장하기위한구조체 */ #pragma pack(1) // 컴파일러의구조체패딩을막기위한선언 typedef struct _PARTITION /* * --------- 구조체변수설명 ------------ * BootFlag : 파티션의부팅가능여부확인 0x80 부팅가능, 0x00 부팅불가능 * CHS_Start : CHS 방식의파티션시작섹터 * Type : 파티션종류 * CHS_End : CHS 방식의파티션종료섹터 * LBA_Start : LBA 방식의파티션시작섹터 * length : 볼륨내총섹터수 * --------------------------------------- */ U8 BootFlag; U8 CHS_Start[3]; U8 type; U8 CHS_End[3]; U32 LBA_Start; U32 length; PARTITION, *PPARTITION; #pragma pack() #pragma pack(1) typedef struct _FAT32_BPB_struct /* * --------- 구조체변수설명 ------------ * 1. BPB 공통영역 (1) * JmpBoot[3] : 부트코드로점프하기위한주소 * OEMName[8] : OEM 회사이름 * BytesPerSec : 섹터당바이트수 * SecPerClus : 클러스터당섹터수 * RsvdSecCnt : 예약된영역의섹터수 * NumFATs : 볼륨내의FAT 영역의갯수로일반적으로 2개 * RootEntCnt : FAT32 에서는 '0' * TotSec16 : FAT32 에서는 '0' * Media : 0xF8 * FATs16 : FAT32 에서는 '0' * SecPerTrk : '0' 더이상참조하지않는다 * NumHeads : '0' 더이상참조하지않는다 * HiddSec : '0' 더이상참조하지않는다 * TotSec32 : 볼륨상에존재하는총섹터수 28 페이지
* 2. BPB FAT32 영역 * FATs32 : FAT 영역의섹터수 * ExtFlags : FAT Table 에대한설정값, 일반적으로0x00 * FileSysVer : FAT32 의버전정보, 0x00 * RootDirClus : 루트디렉토리클러스터의위치 * FileSysInfo : FSInfo 구조체의위치로일반적으로볼륨의 1번섹터에위치 * BootBakSec : BR 의백업클러스터위치 * Reserved[12] : 예약된영역으로 0으로채워진다 * DirNum : 참조하지않는다 * Reserv1 : 0 으로채워진다 * BootSign : 0x29 로고정 * VolID : 볼륨의시리얼번호 * vollabel[11] : 해당파티션의볼륨레이블 * FileSysType[8] : FAT32 라는문자열이들어간다 * 3. BPB 공통영역 (2) * BootCodeArea : BootCode 들어있다. * Signature : BR 의손상여부확인을위해 0xAA55 로고정 * --------------------------------------- */ U8 JmpBoot[3]; U8 OEMName[8]; U16 BytesPerSec; U8 SecPerClus; U16 RsvdSecCnt; U8 NumFATs; U16 RootEntCnt; U16 TotSec16; U8 Media; U16 FATs16; U16 SecPerTrk; U16 NumHeads; U32 HiddSec; U32 TotSec32; // BPB 공통영역 U32 FATs32; U16 ExtFlags; U16 FileSysVer; U32 RootDirClus; U16 FileSysInfo; U16 BootBakSec; U8 Reserved[12]; U8 DirNum; U8 Reserv1; U8 BootSign; U32 VolID; U8 vollabel[11]; 29 페이지
U8 FileSysType[8]; // BPB FAT32 영역 U8 BootCodeArea[420]; U16 Signature; FAT32_BPB; #pragma pack() #pragma pack(1) typedef struct _VOL_struct /* * --------- 구조체변수설명 ------------ * Drive * VolBeginSec * FirstDataSec : 첫번째데이터섹터 * RootDirSec : Root Directory의시작섹터 * RootDirSecCnt : Root Directory의총섹터수 * RootEntCnt : Root Directory의 Directory Entry 개수 * FATSize : FAT 영역의섹터수 * FATStartSec : FAT 영역시작점 * TotalClusCnt : 볼륨의총클러스터수 * TotalSec : 볼륨의총섹터 * DataSecSize : 데이터영역의섹터수 * ClusterSize : 클러스터크기 * SecPerClus : 한개클러스터의섹터수 * --------------------------------------- */ 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; #pragma pack() #pragma pack(1) typedef struct _DIR_struct /* * --------- 구조체변수설명 ------------ 30 페이지
* Name[11] : 파일 / 디렉토리명 + 확장자 * Attr : 파일종류 ex) 0x04 시스템, 0x10 Directory 0x20 Archive File 0xF0 LFNs * NTRes : 0 고정 * CrtTimeTenth : 생성시간 * CrtTime : 생성시간 * CrtDate : 생성일자 * LstAccDate : 접근날짜 * FstClusHi : 파일 / 디렉토리의첫번째클러스터의상위 2Byte * WriteTime : 수정시간 * WriteDate : 수정일자 * FstClustLow : 파일 / 디렉토리의첫번째클러스터의하위 2Byte * FileSize : 파일크기 * --------------------------------------- */ U8 Name[11]; U8 Attr; U8 NTRes; U8 CrtTimeTenth; U16 CrtTime; U16 CrtDate; U16 LstAccDate; U16 FstClusHi; U16 WriteTime; U16 WriteDate; U16 FstClustLow; U32 FileSize; DirEntry; #pragma pack() #pragma pack(1) typedef struct _LONG_DIR_struct /* * --------- 구조체변수설명 ------------ * Order : LFN 순번저장 * Name1+Name2+Name3 : 파일명 * Attr : 0x0F 고정 * Type : 예약으로 0 rhwjd * chksum : Short Directory Entry 의 Checksum 저장 & FstClusLo : 0 고정 * --------------------------------------- */ U8 Order; U8 Name1[10]; U8 Attr; U8 Type; U8 chksum; 31 페이지
U8 Name2[12]; U16 FstClusLo; U8 Name3[4]; LongDirEntry; #pragma pack() /* * ------------- 함수설명----------------- * HDD_read : 저장장치로부터섹터를읽어와메모리에담기위한함수 * HDD_write : 저장장치의섹터에데이터를쓰기위한함수 * HexDump : Dump된 Memory를 Hex로보여주기위한함수 ( 점검용 ) * get_partition : 파티션정보및 BPB 시작주소의획득을위한함수 * get_bpb_info : BPB 의정보를구조체에저장하기위한함수 * print_longname : LFNs를출력하기위한함수 * show_dir : Root Directory Entry를출력하기위한함수 ( 점검용 ) * show_del_dir : 삭제된 Directory Entry를출력하기위한함수 * rec_file : 파일복구를위한함수 * rec_dir : 디렉토리복구를위한함수 * ----------------------------------------- */ U32 HDD_read(U8 drv, U32 SecAddr, U32 blocks, U8* buf); U32 HDD_write(U8 drv, U32 SecAddr, U32 blocks, U8* buf); void HexDump (U8 *addr, U32 len); void get_partition(ppartition ppartition, U8 psecbuf[512]); U32 get_bpb_info(fat32_bpb* BPB, VolStruct* pvol); void print_longname(longdirentry* plongdir, U32 EntryNum); U32 show_dir(direntry* pdir); U32 show_del_dir(direntry* pdir); U32 rec_file(direntry* pdir,u32 rec_entry, U32* fat_entry, U32 Flag, U32 upclus); U32 rec_dir(direntry* pdir, U32 useclus); VolStruct gvol; int main() /* * ------------- 변수설명----------------- * mbr_buf : mbr 영역의덤프를위한버퍼 * bpb_buf : bpb 영역의덤프를위한버퍼 * fat_buf : fat 영역의덤프를위한버퍼 * ppartition_arr : 파티션정보저장을위한구조체변수 * root_buf : Root Directory Entry 영역의덤프를위한버퍼 * sel_m_menu : 메인메뉴에서의선택을위한변수 * sel_rec_entry : 복구하고자하는디렉토리엔트리입력변수 * ----------------------------------------- 32 페이지
*/ U32 sel_m_menu; U32 sel_rec_entry; U8 mbr_buf[512]; U8 bpb_buf[512]; U32 fat_buf[128]; U8* root_buf; PARTITION ppartition_arr[50]; gvol.drive = 0x2; gvol.volbeginsec = 0x0; // 초기 HDD 덤프를위한장치번호와시작섹터초기화 if(hdd_read(gvol.drive, gvol.volbeginsec, 1, mbr_buf)== 0) printf( "Boot Sector Read Failed \n" ); return 1; // mbr 영역의덤프를위한 HDD_read 함수호출 get_partition(ppartition_arr, mbr_buf); // mbr 덤프를이용한파티션정보습득을위한 get_partition 함수호출 gvol.volbeginsec = ppartition_arr->lba_start; // 시작위치를 Partition의시작섹터로변경 if(hdd_read(gvol.drive, gvol.volbeginsec, 1, bpb_buf)==0) printf( "BPB Sector Read Failed \n"); return 1; // BPB 영역의덤프를위한 HDD_read 함수호출 if(get_bpb_info((fat32_bpb *)bpb_buf, &gvol) == 0) printf( "It is not FAT32 File System \n" ); return 1; // BPB 영역의정보를구조체에저장하기위한 get_bpb_info 함수호출 gvol.rootdirseccnt = 10; gvol.rootentcnt = 100; // Root Directory Entry 내섹터를읽어오기위한변수설정 /* * ---------------- 개선해야할사항 ---------------- 33 페이지
* 단고정된값이아니라가변적인값으로처리할방법을구상해야함 * ------------------------------------------------- */ root_buf = (U8*)malloc(gVol.RootDirSecCnt*512); // Root Directory Entry 공간만큼의동적할당 if(hdd_read(gvol.drive, gvol.rootdirsec, gvol.rootdirseccnt, root_buf)==0) printf("root Directory Read Failed \n"); return 1; printf("============= USB Recovery Tool Ver.FAT32 =============\n"); printf("1. Analyze USB \n"); printf("2. Exit \n"); printf("select 1 or 2 : "); scanf("%d", &sel_m_menu); // 메인메뉴출력및변수입력 switch(sel_m_menu) case 1: show_del_dir((direntry*)root_buf); break; case 2: exit(1); // 메인메뉴입력변수에따른분기를위한 switch 문 // 1 : 지워진파일 / 디렉토리출력 // 2 : 프로그램종료 printf("\n\n============= Recovery Mode =============\n"); printf(" 복구하고자하는파일의 Entry Number를입력하세요 : "); scanf("%d", &sel_rec_entry); // 복구모드출력및복구할 Directory Entry 선택 if(hdd_read(gvol.drive,gvol.fatstartsec, 1, fat_buf)==0) printf( "FAT Sector Read Failed \n"); return 1; // FAT 영역의덤프를위한 HDD_read 함수호출 rec_file((direntry*)root_buf,sel_rec_entry,fat_buf, 0, 0); // 데이터의복구를위한 rec_file 함수호출 34 페이지
return 0; U32 HDD_read(U8 drv, U32 SecAddr, U32 blocks, U8* buf) /* * ------------- 변수설명----------------- * drv : 접근하고자하는물리장치 * SecAddr : 시작섹터설정을위한변수 * blocks : 읽어올섹터수 * buf : 해당섹터에대한 dumpdata * ret : 해당장치의존재유무반환용변수 * ldistancelow : 하위23bit 저장 * ldistancehigh : 상위9bit 저장 * dwpointer : 시작섹터 * bytestoread : 파일을읽을단위설정 * numread : 읽은바이트수 * cur_drv : 접근하고자하는드라이브명 ex) \\.\PhysicalDrive0 * g_hdevice : 파일핸들 * ----------------------------------------- */ U32 ret; U32 ldistancelow, ldistancehigh, dwpointer, bytestoread, numread; char cur_drv[100]; HANDLE g_hdevice; sprintf(cur_drv, "\\\\.\\PhysicalDrive%d",(U32)drv); // CreateFIle 을통한 PhysicalDrive 로의접근을위한문자열생성 g_hdevice = CreateFile(cur_drv, GENERIC_READ, FILE_SHARE_READ FILE_SHARE_WRITE, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); // CreateFile을활용한PhysicalDrive 접근용파일핸들생성 if(g_hdevice==invalid_handle_value) return 0; // 해당디바이스존재여부에따른분기 ldistancelow = SecAddr << 9; ldistancehigh = SecAddr >> (32-9); // ldistancelow 하위 9 bit, ldistancehigh 상위 23 bit 저장 dwpointer = SetFilePointer(g_hDevice, ldistancelow, ( long *)&ldistancehigh, FILE_BEGIN); // 시작섹터설정 35 페이지
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 HDD_write(U8 drv, U32 SecAddr, U32 blocks, U8* buf) /* * ------------- 변수설명----------------- * drv : 접근하고자하는물리장치 * SecAddr : 시작섹터설정을위한변수 * blocks : 읽어올섹터수 * buf : 해당섹터에대한 dumpdata * ret : 해당장치의존재유무반환용변수 * ldistancelow : 하위23bit 저장 * ldistancehigh : 상위9bit 저장 * dwpointer : 시작섹터 * bytestoread : 파일을읽을단위설정 * numread : 읽은바이트수 * cur_drv : 접근하고자하는드라이브명 ex) \\.\PhysicalDrive0 * g_hdevice : 파일핸들 * ----------------------------------------- */ U32 ret = 0; U32 ldistancelow, ldistancehigh, dwpointer, bytestoread, numread; char cur_drv[100]; HANDLE g_hdevice; sprintf(cur_drv, "\\\\.\\PhysicalDrive%d",(U32)drv); // CreateFIle 을통한 PhysicalDrive 로의접근을위한문자열생성 g_hdevice = CreateFile(cur_drv, GENERIC_WRITE, FILE_SHARE_READ FILE_SHARE_WRITE, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); // CreateFile을활용한PhysicalDrive 접근용파일핸들생성 if(g_hdevice==invalid_handle_value) return 0; 36 페이지
// 해당디바이스존재여부에따른분기 ldistancelow = SecAddr << 9; ldistancehigh = SecAddr >> (32-9); dwpointer = SetFilePointer(g_hDevice, ldistancelow, ( long *)&ldistancehigh, FILE_BEGIN); // 시작섹터설정 if(dwpointer!= 0xFFFFFFFF) bytestoread = blocks * 512; // 읽어올데이터길이 ret = WriteFile(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 U32 *s=addr, *endptr=(u8*)((u32)addr+len); 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"); 37 페이지
// 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( " " ); return; // HexDump. 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"); void get_partition(ppartition ppartition, U8 psecbuf[512]) memcpy(ppartition, (psecbuf + PARTITION_TBL_POS), sizeof(partition)); // Partition 추출 U32 get_bpb_info(fat32_bpb* BPB, VolStruct* pvol) if(bpb->rootentcnt!= 0 BPB->Signature!= 0xAA55) return 0; // Root Directory & Signature 의이상유무를판단하여분기 pvol->totalsec = BPB->TotSec32; // Get Total Sector pvol->fatsize = BPB->FATs32; // Get FAT Size pvol->fatstartsec = pvol->volbeginsec + BPB->RsvdSecCnt; // Get FAT Start Sector 38 페이지
pvol->rootentcnt = BPB->RootEntCnt; //Get Root Directory Entry Count pvol->rootdirsec = pvol->volbeginsec + BPB->RsvdSecCnt + (BPB->NumFATs * BPB->FATs32); //Get Root Directory Sector pvol->firstdatasec = pvol->volbeginsec + BPB->RsvdSecCnt + (BPB->NumFATs * pvol->fatsize) + pvol- >RootDirSecCnt; // Get FAT Start Sector pvol->datasecsize = pvol->totalsec - (BPB->RsvdSecCnt + (BPB->NumFATs * pvol->fatsize) + pvol- >RootDirSecCnt); // Get Size Of Data Area pvol->totalcluscnt = pvol->datasecsize / BPB->SecPerClus; //Get Total Cluster Count pvol->clustersize = BPB->SecPerClus * BPB->BytesPerSec; //Get Size Of Cluster pvol->secperclus = BPB->SecPerClus; // Get Sector Per Cluster return 1; U32 show_dir(direntry* pdir) /* * ------------- 변수설명----------------- * i,j : 반복변수 * LongEntryEn : LFNs 검출용변수 * ----------------------------------------- */ U32 i, j, LongEntryEn=0; for(i=0;i<=gvol.rootentcnt;i++) switch((u8) pdir[i].name[0]) case 0x00: // End Of Entry return 1; case 0xE5 : // delete of Entry continue; // Directory Entry 의상태확인 if(pdir[i].attr == 0x0F) LongEntryEn = 1; 39 페이지
// Long File Name Entry continue; // LFNs Directory Entry 구분 printf("------------------------- Entry Number %d -------------------------\n",i); if(pdir[i].attr == 0x10) printf("directory Name : "); else printf("file Name : "); // Directory or File Name is if(longentryen == 1) print_longname((longdirentry*)pdir, i-1); LongEntryEn = 0; // LFNs 인경우의파일 / 디렉토리이름출력 else for(j=0;j<11;j++) printf("%c",pdir[i].name[j]); // Short Name 출력 printf("\n"); return 1; printf("file Size : %d\n",pdir[i].filesize); printf("start Cluster : %d\n",(pdir[i].fstclustlow pdir[i].fstclushi << 16)); // First Cluster Low/High 를전체출력 void print_longname(longdirentry* plongdir, U32 EntryNum) /* * ------------- 변수설명----------------- * filename : LFNs 파일명으로합치기위한배열 (Name1 + Name2 + Name3) * final : filename을유니코드방식으로변경한최종파일명 * nameoffset : LFNs Entry 에서의 Name%d 이동을위한변수 * ----------------------------------------- */ wchar_t filename[512]; char final[512]; U32 nameoffset = 0; do 40 페이지
memcpy(&filename[nameoffset],plongdir[entrynum].name1, 10); // Name1에저장된이름문자열복사 nameoffset += 5; // Name1 속성에서 Name2 속성으로이동 memcpy(&filename[nameoffset],plongdir[entrynum].name2, 12); // Name2에저장된이름문자열복사 nameoffset += 6; // Name2 속성에서 Name3 속성으로이동 memcpy(&filename[nameoffset],plongdir[entrynum].name3, 4); // Name3에저장된이름문자열복사 nameoffset += 2; while((plongdir[entrynum--].order & 0x40)==0); // 0x40 과 or 연산한것이가장마지막 LFNs filename[nameoffset] = 0x0000; // 문자열의끝을알리는 0x0000 저장 wcstombs(final,filename, 512); // 유니코드 -> 아스키코드로변환 printf("%s",final); U32 show_del_dir(direntry* pdir) /* * ------------- 변수설명----------------- * i,j : 반복변수 * LongEntryEn : LFNs 검출용변수 * ----------------------------------------- */ U32 i, j, LongEntryEn=0; for(i=0;i<=gvol.rootentcnt;i++) if((u8) pdir[i].name[0] == 0x00) return 1; else if((u8) pdir[i].name[0]!= 0xE5) continue; // Directory Entry 의상태확인 if(pdir[i].attr == 0x0F) LongEntryEn = 1; // Long File Name Entry 41 페이지
continue; // LFNs Directory Entry 구분 printf("------------------------- Entry Number %d -------------------------\n",i); //if(pdir[i].attr!= 0x04 if(pdir[i].attr == 0x10) printf("directory Name : "); else printf("file Name : "); // Directory or File Name is if(longentryen == 1) print_longname((longdirentry*)pdir, i-1); LongEntryEn = 0; // LFNs 인경우의파일 / 디렉토리이름출력 else for(j=0;j<11;j++) printf("%c",pdir[i].name[j]); // Short Name 출력 printf("\n"); return 1; printf("file Size : %d\n",pdir[i].filesize); printf("start Cluster : %d\n",(pdir[i].fstclustlow pdir[i].fstclushi << 16)); // First Cluster Low/High 를전체출력 U32 rec_file(direntry* rdata, U32 rec_entry, U32* fat_entry, U32 Flag, U32 upclus) /* * ------------- 변수설명----------------- * i : 반복변수 * useclus : 빈클러스터 * reset_buf : 빈클러스터초기화용버퍼 * rec_buf : 파일내용백업용버퍼 * dir_buf : 디렉토리를복구할경우의서브디렉토리의 Directory Entry 덤프를위한버퍼 * FstClustNum : 첫번째클러스터를저장하기위한변수 * StartSec : 복구시작섹터저장을위한변수 -> 최초에는 Root Directory Sector에서복구가진행되지만 * 그이후부터는 Sub Directory에서진행되기때문에고정값 X * Flag : 0 -> Root Directory, 1 -> Sub Directory * upclus : 상위디렉토리의클러스터번호 42 페이지
* ----------------------------------------- */ U32 i, useclus; U8 reset_buf[4096]; U8 rec_buf[4096]; U8 dir_buf[4096]; U32 FstClustNum; U32 StartSec; memset(reset_buf, 0x00, sizeof(reset_buf)); // 초기화용메모리제작 (0x00 으로전체초기화시킴 ) memset(rec_buf, 0x00, sizeof(rec_buf)); // 복제메모리초기화 FstClustNum = rdata[rec_entry].fstclustlow rdata[rec_entry].fstclushi << 16; // First Cluster Total 저장 HDD_read(gVol.Drive, gvol.rootdirsec + (FstClustNum - 2)*gVol.SecPerClus, 8, rec_buf); // 파일내용복사 for(i=0;;i++) if(fat_entry[i] == 0x00000000) useclus = i; // 빈클러스터발견시해당클러스터번호 useclus에저장 break; // for(;;) // FAT 영역에서의빈클러스터검색 // -------------------------------------------------------------------------------------- 삭제파일복구 ( 적용 X) fat_entry[useclus] = 0x0FFFFFFF; // 빈클러스터를사용클러스터로변경 rdata[rec_entry].name[0] = 'R'; // 삭제시그니쳐 0xE5 -> R 문자열로변경 // -------------------------------------------------------------------------------------- 삭제파일복구 ( 적용 X) // -------------------------------------------------------------------------------------- 복구내용적용구간 HDD_write(gVol.Drive, gvol.rootdirsec + (useclus-2)*gvol.secperclus, 8, reset_buf); // 사용할클러스터초기화 if(flag == 0) StartSec = gvol.rootdirsec; Flag = 1; 43 페이지
else StartSec = gvol.rootdirsec + (upclus - 2 ) * gvol.secperclus; // Root Directory와 Sub Directory 에따른 Directory Entry 의시작주소변경을위한분기 rdata[rec_entry].fstclustlow = useclus; // 삭제이전클러스터 -> 사용할클러스터 HDD_write(gVol.Drive, gvol.rootdirsec + (useclus-2)*gvol.secperclus, 8, rec_buf); // 백업해둔파일내용새로운클러스터에복사 HDD_write(gVol.Drive, StartSec, 8, rdata); // 삭제시그니쳐 -> R 시그니처적용 HDD_write(gVol.Drive, gvol.fatstartsec, 8, fat_entry); // 빈클러스터 -> 사용클러스터변경내용적용 // -------------------------------------------------------------------------------------- 복구내용적용구간 /* * ---------------- 개선해야할사항 ---------------- * 해당파일의크기가 1 Cluster 를넘어갈때의복구 * ------------------------------------------------- */ if(rdata[rec_entry].attr == 0x10) StartSec = gvol.rootdirsec + (FstClustNum - 2 ) * gvol.secperclus; // Sub Directory Entry 의시작위치 HDD_read(gVol.Drive, StartSec, 8, dir_buf); // Sub Directory Entry Dump rec_dir(dir_buf, Flag, useclus); // Directory 에대한복구를위한 rec_dir 함수호출 // 해당 Directory Entry가 Directory 일경우의복구 printf("%s 이 / 가복구완료되었습니다. \n", rdata[rec_entry].name); // 복구된파일 / 디렉토리명과완료메세지출력 return 0; U32 rec_dir(direntry* pdir, U32 Flag, U32 useclus) /* * ------------- 변수설명----------------- * i : 반복변수 * pdir : Sub Directory 의 Dump 를담는버퍼 * U32 Flag : 0 -> Root Directory, 1 -> Sub Directory * useclus : 현재디렉토리의클러스터번호 * FstClustNum : 해당 Directory Entry 가사용하는클러스터번호 (!= useclus) * fat_buf : fat Table Entry를담기위한버퍼 44 페이지
* ----------------------------------------- */ U32 i; U32 FstClustNum; U32 fat_buf[128]; for(i=0;pdir[i].name[0]!=0x00;i++) FstClustNum = pdir[i].fstclustlow pdir[i].fstclushi << 16; if(fstclustnum == 0x00000000 pdir[i].name[0]!= 0xE5) continue; // 클러스터할당여부확인 else if(hdd_read(gvol.drive,gvol.fatstartsec, 1, fat_buf)==0) printf( "FAT Sector Read Failed \n"); return 1; // FAT 영역의덤프를위한 HDD_read 함수호출 rec_file(pdir, i, fat_buf, Flag, useclus); return 0; 4.2. EXCUTING PROGRAM 4.2.1. Main Menu [ 그림 4-1] Main Menu 프로그램의메인메뉴는 [ 그림 4-1] 과같으며 1. Analyze USB 메뉴를선택하면 USB 에대한 분석이시작된다. 4.2.2. Directory Entry 확인 45 페이지
[ 그림 4-2] 분석결과 분석결과는 [ 그림 4-2] 와같은데분석결과는삭제된파일 / 디렉토리목록을출력해준다. 이중에서살리고자하는 Entry Number 를입력하되만약디렉토리라면내부의파일 / 디렉토리가 전부복구된다. [ 그림 4-3] 복구모드 복구모드의결과로복구된파일 / 디렉토리목록을출력해준다. 4.2.3. 복구결과확인 [ 그림 4-4] 복구결과확인 [ 그림 4-4] 와같이 Dirtest 디렉토리를포함하여그내부의 filetest.txt 파일까지복구되는것을 확인할수있다. 46 페이지
4.3. 향후계획 - 일정량이상의 Cluster 크기를할당받아야하는파일 / 디렉토리에대한복구구현 47 페이지