-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathDiskAnalysis.cpp
2797 lines (2159 loc) · 82.3 KB
/
DiskAnalysis.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
Class CDiskAnalysis()
The basis of this class is to recieve either a handle to an open (disk, volume, file)
or a string to one of the above to be opened by this class.
This class performs functions such as determining the type of formatting on a disk
getting info from the MasterBootRecord & Partition Table, Sector Sizes, and the
PartitionBootTable.
On contruction of this class variables are initlised but no disk access starts until
the member function AnalyzeDisk is called, then other member functions can be called
to gather info.
How To Use This Class
---------------------
1. Create an instance of this class in your code e.g CDiskAnalysis myDisk;
2. Call required function(s)
CORE FUNCTIONS:
==============================================================================================
TO COMPLETE:
===========
FindSectorMFT() - Internal - Find the given sector in the MFT and translate to file details
FindSector() - Public - Called from client to get details of a given sector
PUBLIC:
=======
FindFile() - Find a file on the specified logical drive.
RecoverFile() - Recovers file using FILE_CLUSTERS struct info
FillPartitionInfo() - Uses the name 'PhysicalDiskX' to open the disk and get the MBR info
PRIVATE:
========
GetMBR() - Fills The MASTER_BOOT_RECORD Structure with the Master Boot Record Details
GetPartitionTable() - Fills the PARTITION_TABLE_NTFS structure with the selected NTFS partition boot sector details
GetMFTOffsets() - Creates an array of MFT_CLUSTERS defining MFT cluster on disk.
FindFileMFT() - Internal Function To Get A File From The NTFS MFT into struct FILE_CLUSTERS
GetFileExtents() - Internal - Gets the File Extents from NTFS MFT into struct FILE_EXTENT
GetFilePathMFT() - Internal - Get The DOS path for the seleted file from the NTFS MFT
GetLogicalDisks() - Gets all logical disks assigned in this system into struct PHYSICAL_DISKS
GetPhysicalDisks() - Gets all physical disks installed in this system into struct LOGICAL_DISKS
ON CONSTRUCTION:
================
GetPhysicalDisks() - Populates struct PHYSICAL_DISKS with each disk name (CdRom1, PhysicalDisk0, etc)
GetLogicalDisks() - Populates struct LOGICAL_DISKS with log disk, phy disk#,start cluster offset (c:\, 0, 63)
NOTES:
======
1. Using the GetMFTOffsets Function:
diskanalysis.GetMFTOffsets(); // Find All The Sectors That Make The MFT
MFT_CLUSTERS *pMFT;
pMFT = diskanalysis.m_ClusterArray;
__int64 itest = pMFT->iStartCluster;
pMFT++;
__int64 itest2 = pMFT->iStartCluster;
2. Using the FindFile Function:
CDiskAnalysis diskanalysis;
CString strFile = "shell32.dll" ;
diskanalysis.FindFile(strFile, "C:\\", NULL))
*/
#include "StdAfx.h"
#include "diskanalysis.h"
#include "diskioctl.h"
#include "winioctl.h"
#include "cstringt.h"
#include "math.h"
struct{
int iIndex;
DWORD dwType;
TCHAR* szLabel;
}
FormatTypes[] =
{
0,0x00, _T("Unknown Type"),
1, 0x01, _T("FAT12 primary partition or logical drive (fewer than 32,680 sectors in the volume)"),
2, 0x04, _T("FAT16 partition or logical drive (32,680–65,535 sectors or 16 MB–33 MB)"),
3, 0x05, _T("Extended partition"),
4, 0x06, _T("BIGDOS FAT16 partition or logical drive (33 MB–4 GB)"),
5, 0x07, _T("Installable File System (NTFS partition or logical drive)"),
6, 0x0B, _T("FAT32 partition or logical drive"),
7, 0x0C, _T("FAT32 partition or logical drive using BIOS INT 13h extensions"),
8, 0x0E, _T("BIGDOS FAT16 partition or logical drive using BIOS INT 13h extensions"),
9, 0x0F, _T("Extended partition using BIOS INT 13h extensions"),
10, 0x12, _T("EISA partition or OEM partition"),
11,0x42, _T("Dynamic volume"),
12,0x84, _T("Power management hibernation partition"),
13,0x86, _T("Multidisk FAT16 volume created by using Windows NT 4.0"),
14,0x87, _T("Multidisk NTFS volume created by using Windows NT 4.0"),
15,0xA0, _T("Laptop hibernation partition"),
16,0xDE, _T("Dell OEM partition"),
17,0xFE, _T("IBM OEM partition"),
18,0xEE, _T("GPT partition"),
19,0xEF, _T("EFI System partition on an MBR disk"),
} ;
const char tabAlpha[26] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
CDiskAnalysis::CDiskAnalysis(void)
{
m_HndData = NULL;
//m_iSize = 0;
m_iDiskOpen = -1; // no open disk
m_HandleType = OPEN_NONE;
m_ClusterArray = (MFT_CLUSTERS*)malloc(1 * sizeof(MFT_CLUSTERS) ); // Allocate
m_ClusterArray->iStartCluster = 0;
m_ClusterArray->iEndCluster = 0;
m_ClusterArray->iMFTRecSize = 0;
// change to use VirtualAlloc
//m_FileDetails = (FILE_CLUSTERS*)malloc(1 * (sizeof(FILE_CLUSTERS) )); // Allocate
//m_FileDetails->sFileExtents = (FILE_EXTENT*)malloc(1 * sizeof(FILE_EXTENT) );
//m_FileDetails = (FILE_CLUSTERS*)VirtualAlloc(NULL, sizeof(FILE_CLUSTERS)*500, MEM_COMMIT, PAGE_READWRITE);
//m_FileDetails->sFileExtents = (FILE_EXTENT*)VirtualAlloc(NULL, 500 *sizeof(FILE_EXTENT), MEM_COMMIT, PAGE_READWRITE);
// m_FileDetails->iIsDeleted = FALSE;
// memset(m_FileDetails->strFolder ,'0', 260) ;
// m_FileDetails->strFolder[260] = '\0';
// m_FileDetails->sFileExtents->iFileExtentEndCluster = 0;
// m_FileDetails->sFileExtents->iFileExtentStartCluster = 0;
// m_FileDetails->sFileExtents->iMFTRecordNo = 0;
m_physicaldisks = (PHYSICAL_DISKS*)malloc(1 * sizeof(PHYSICAL_DISKS) ); // Allocate
m_logicaldisks = (LOGICAL_DISKS*)malloc(1 * sizeof(LOGICAL_DISKS) );
hGlobalPT = NULL;
// Get The installed disk details, this will call GetLogical Disks As Well
// TODO : Do we need a modeless box here to tell the user we are gathering data ?
GetPhysicalDisks();
}
CDiskAnalysis::~CDiskAnalysis(void)
{
// Cleanup
if(m_HndData != NULL)
CloseHandle(m_HndData);
free(m_physicaldisks);
free(m_logicaldisks);
free(m_ClusterArray);
//free(m_FileDetails);
}
/*
Class Functions
*/
/***************************************************************************************
Functions Non Specific To OS
.Getting Master Boot Record
*****************************************************************************************/
/*
Open Disk/Volume/File
OS: 95/98/ME cant exceed MAX_PATH for file name string TODO
*/
BOOL CDiskAnalysis::OpenHandle(LPCTSTR lpctPath)
{
/* Check to see what type of path has been passed to us
or do we do this ourselves, ie we open each phydisk to get the info
needed to fill the tree struct.
We have two options:
1. Make this class specific to the tree control needs
2. Make it self supporting and deal with different possible needs
Currently we have to open a physical disk
*/
m_HndData = CreateFile(lpctPath, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, 0);
if(m_HndData == INVALID_HANDLE_VALUE) // Function Failed
{
DWORD dwErr = GetLastError();
//TRACE1("Failed To Open File - DiskAnalysis.cpp-OpenHandle() Error:%i", dwErr);
return false;
}
CString strTemp;
//GetDriveType("A:");
if(strstr(lpctPath, "Volume") != NULL)
m_HandleType = OPEN_VOLUME ;
char *pDest = strstr(lpctPath, ":");
if(pDest != NULL)
{
int result = (int)(pDest - lpctPath + 1);
int string = (int)strlen(lpctPath); // for a volume the char : should be the last char in the file open str
if(result == string)
m_HandleType = OPEN_LOGICAL ;
}
if(strstr(lpctPath, "Physical") != NULL || strstr(lpctPath, "CdRom") != NULL)
{
m_HandleType = OPEN_PHYSICAL ;
CString temp = lpctPath;
m_iDiskOpen = (int)lpctPath[strlen(lpctPath)]; // get phydisk number
return (GetMBR() > 0 ? true : false);
}
if(m_HandleType == OPEN_NONE)
m_HandleType = OPEN_FILE;
return true;
}
/*
Get The Master Boot Record (MBR) and Fill In Data Struct
Gets Info About the first four partitions on this disk and
puts this details in to a public array of m_HDPartitionTable[x]
where x is the partition no
System Partition is specified by the bBootable data member of the m_HDPartitionTable struct
OS: Not OS Specific
*/
int CDiskAnalysis::GetMBR(BYTE bParent, DWORD64 dwRelSector, DWORD dwRecno)
{
DWORD dwErr = 0;
DWORD dwType;
BOOL bSuccess;
DWORD lpdRead;
BYTE* byData; // Data From ReadFile
DWORD iLowBits, iHighBits;
int iOffset = 446;
char* strText = NULL;
// must be a physical disk only
ASSERT((m_HandleType & ~OPEN_PHYSICAL) == 0);
if((m_HandleType & ~OPEN_PHYSICAL) != 0)
{
AfxMessageBox(MBR_PHYSICAL_ONLY, MB_ICONSTOP, 0);
return 0;
}
// Save current file pointer
PLARGE_INTEGER pli;
pli = (PLARGE_INTEGER)VirtualAlloc(NULL, sizeof(LARGE_INTEGER), MEM_COMMIT, PAGE_READWRITE);
LARGE_INTEGER li;
li.HighPart = 0; li.LowPart = 0; li.QuadPart = 0;
SetFilePointerEx(m_HndData, li, pli, FILE_CURRENT);
// Set File Pointer To Beginning Of Disk
li.HighPart = 0; li.LowPart = 0; li.QuadPart = (dwRelSector*512);
dwErr = SetFilePointerEx(m_HndData, li, NULL , FILE_BEGIN);
if(dwErr == INVALID_SET_FILE_POINTER)
{
dwErr = GetLastError();
//TRACE1("Failed To Set File Pointer - DiskAnalysis.cpp-GetMBR() Error:%i", dwErr);
return 0;
}
byData = (BYTE*)VirtualAlloc(NULL, 512, MEM_COMMIT, PAGE_READWRITE);
// Read In 512 Bytes - (Always the same for all disks for the MBR)
bSuccess = ReadFile(m_HndData, byData, 512, &lpdRead, NULL);
if(!bSuccess)
{
dwErr = GetLastError();
//TRACE1("\nFailed To Get File Data - DiskAnalysis.cpp-GetMBR() Error:%i", dwErr);
return 0;
}
if( ( *(byData + 510) != 85) && // 55(85) AA(170) is the MBR signature
( *(byData + 511) != 170))
{
// TODO : DiskStatusLog(MBR_MISSING, MB_ICONSTOP, 0);
// restore file pointer
li.HighPart = pli->HighPart; li.LowPart = pli->LowPart; li.QuadPart = pli->QuadPart;
SetFilePointerEx(m_HndData, li, NULL, FILE_BEGIN);
return 0; // invalid MBR - does not exist (cdrom) or corrupt/blank (harddisk)
}
/*********************************************************************************************************************
NOTE CHANGE TO HEAPS**
*********************************************************************************************************************/
// strText = (char*)malloc(2);
BOOL bChild = false;
byData += 430; // offset to 1st partition partition table in the MBR - 16 bytes
int iCount = 0, iIndex = 0;
char czBuf[2];
while(true)
{
iCount++;
byData += 16; // next entry
if((*(byData+12) +
(*(byData+13) << 8) +
(*(byData+14) << 16) +
(*(byData+15) << 24)) == 0) // no sectors - invalid record
{
// restore file pointer
li.HighPart = pli->HighPart; li.LowPart = pli->LowPart; li.QuadPart = pli->QuadPart;
SetFilePointerEx(m_HndData, li, NULL, FILE_BEGIN);
//free(strText);
if(bParent == 0) // back to root or first part table, add a final zero init'd record.
{
dwRecno++;
HGLOBAL hPrev = hGlobalPT;
hPrev = GlobalReAlloc(hGlobalPT, (dwRecno*sizeof(MASTER_BOOT_RECORD)), GMEM_MOVEABLE|GMEM_ZEROINIT );
dwErr = GetLastError();
ASSERT(dwErr == 0);
if(dwErr == 0)
hGlobalPT = hPrev;
}
return dwRecno;
}
dwRecno++; // next valid record number
if(hGlobalPT == NULL)
hGlobalPT = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, sizeof(MASTER_BOOT_RECORD)); // 1 * MBR Partition Record
else
hGlobalPT = GlobalReAlloc(hGlobalPT, (dwRecno*sizeof(MASTER_BOOT_RECORD)), GMEM_MOVEABLE|GMEM_ZEROINIT );
ASSERT(hGlobalPT != NULL);
m_HDPartitionTable = (MASTER_BOOT_RECORD*)GlobalLock(hGlobalPT);
m_HDPartitionTable += (dwRecno-1); // move to next blank record
m_HDPartitionTable->bParent = bParent;
m_HDPartitionTable->bRecordNo = dwRecno;
// bootable partition
m_HDPartitionTable->bBootable = (*(byData) == 0x80 ? true : false);
m_HDPartitionTable->byStartHead = *(byData+1);
m_HDPartitionTable->iStartSector = *(byData+2) & 63; // get low 6 bits
m_HDPartitionTable->iStartCylinder = (*(byData+3) << 2) + (*(byData+2) & 3); // shift offset 3 by two and add
dwType = *(byData+4);
m_HDPartitionTable->iFileSystem = dwType; // Hex of installed file system.
//delete strText;
//strText = (char*)malloc(255);
//strText = (char*)memset(strText, '0', 255);
if(strText != NULL)
VirtualFree(strText, 0, MEM_RELEASE);
strText = (char*)VirtualAlloc(NULL, 255, MEM_COMMIT, PAGE_READWRITE);
iIndex = GetFormatTypeText(dwType); // MAX_PATH
memcpy(m_HDPartitionTable->strFileSystem, FormatTypes[iIndex].szLabel, strlen(FormatTypes[iIndex].szLabel) ); // Description Of Format Type On Disk
m_HDPartitionTable->iEndingHead = *(byData+5); // Ending Head
m_HDPartitionTable->iEndingSector = *(byData+6) & 63; // AND use '0' in binary to clear opposing bits to 0
m_HDPartitionTable->iEndingCylinder = (*(byData+7) << 2) + (*(byData+6) & 3);
m_HDPartitionTable->dwRelativeSector = *(byData+8) +
(*(byData+9) << 8) +
(*(byData+10) << 16) +
(*(byData+11) << 24) ;
m_HDPartitionTable->dwTotalSectors = *(byData+12) +
(*(byData+13) << 8) +
(*(byData+14) << 16) +
(*(byData+15) << 24);
// valid partition
m_HDPartitionTable->bValidPartition = (m_HDPartitionTable->dwTotalSectors > 0 ? true : false);
m_HDPartitionTable->dwFormatType = GetPartitionFormatType(m_HDPartitionTable->dwRelativeSector);
_itoa(iCount,czBuf, 10) ;
czBuf[1] = '\0';
if(*(byData+4) == 0x05 || *(byData+4) == 0x0F) // this is an extended partition entry, go to this entry and get info
{
strcpy(m_HDPartitionTable->strResourceDesc, "Partition ");
strcat(m_HDPartitionTable->strResourceDesc, czBuf);
BYTE bRecno = m_HDPartitionTable->bRecordNo ;
DWORD64 dwSector = m_HDPartitionTable->dwRelativeSector ;
GlobalUnlock(hGlobalPT);
VirtualFree(strText, 0, MEM_RELEASE);
VirtualFree(byData, 0, MEM_RELEASE);
VirtualFree(pli, 0, MEM_RELEASE);
dwRecno = GetMBR(bRecno, dwSector , dwRecno);
}
else
{
strcpy(m_HDPartitionTable->strResourceDesc, "Volume ");
strcat(m_HDPartitionTable->strResourceDesc, czBuf);
GlobalUnlock(hGlobalPT);
}
//dwRecno++;
}
// should never get here !
// restore file pointer
li.HighPart = pli->HighPart; li.LowPart = pli->LowPart; li.QuadPart = pli->QuadPart;
SetFilePointerEx(m_HndData, li, NULL, FILE_BEGIN);
//free(strText);
return true;
}
/***************************************************************************************
Functions Specific To NTFS MFT
*****************************************************************************************/
/*
Get The File Extents / What Clusters The File Is Held In
OS : NTFS Supporting OS (NT/2000/XP)
Params:
bData - Pointer to data BYTE array for this MFT Record
iExtentOffset - $DATA attribute offset from the start of the MFT record
sFileExtents - Pointer to FILE_CLUSTERS array to be filled by this func
*/
int CDiskAnalysis::GetFileExtents(BYTE *bData, int iExtentOffset, FILE_CLUSTERS* sFileExtents)
{
/*************************************************************************************************
THE DATA RUNS CAN BE AT DIFFERENT OFFSETS FROM THE START OF THE DATA HEADER 0x80
NO CONSISTENT DOCS ARE AVAILABLE TO SHOW EXACT FORMAT/CALC TO FIGURE THIS OUT...
BELOW IS THE CALC THAT WORKS. - 32 Bytes from the start of the $DATA attribute
**************************************************************************************************/
int iDataRunOffset = bData[32+iExtentOffset]; // iDataRunOffset will now have the offset of the start of the DRuns
// Offset From Attribute Header 0x80 $DATA.
// iExtentOffset has the Attribute Header (0x80 $DATA)Offset position
int iOff = iExtentOffset + iDataRunOffset; // Take the Start of the Attribute Header (0x80 $ DATA) and add the offset
// value of the data runs from the header and we end up with the
// position of the start of the extent runs in the MFT file.
int iCnt = 0, j=0, k=0;
__int64 iTemp=0;
int pdwLow = 0, ifcSize = -1;
DWORD dwHigh=0, dwLow=0;
DWORD dwTemp=0;
FILE_EXTENT *m_fc;
FILE_EXTENT *m_de;
FILE_CLUSTERS *m_fc2;
FILE_EXTENT *fcTemp;
// do a quick smaler version of the next loop to find how many extents we have
// and use that to calc mem needed to store them..
while(bData[iOff] > 0) // Last Extent will end with 0x00
{
dwHigh = (bData[iOff] & 15 ); // Highbits - Byte Length of run
dwLow = (bData[iOff] & 240); // Low Bits - Byte Length of cluster
dwLow = (dwLow >> 4); // Bitshift 4 to low bits
iOff += (dwHigh + dwLow+1 );
iCnt++;
}
m_fc2 = sFileExtents;
// check to see if we have any existing extents in this struct
while(m_fc2->iNoExtents > 0)
{
// allocate memory
fcTemp = (FILE_EXTENT*)VirtualAlloc(
NULL, // next page to commit
m_fc2->iNoExtents*sizeof(FILE_EXTENT), // page size, in bytes
MEM_COMMIT, // allocate a committed page
PAGE_READWRITE);
m_fc = sFileExtents->sFileExtents;
// save current extents
memcpy(fcTemp, m_fc, (m_fc2->iNoExtents+1)*sizeof(FILE_EXTENT));
iCnt += (m_fc2->iNoExtents);
ifcSize = (m_fc2->iNoExtents);
m_fc2->iNoExtents = 0;
}
// Allocate memory
sFileExtents->sFileExtents = (FILE_EXTENT*)VirtualAlloc(
NULL, // next page to commit
(iCnt+1)*sizeof(FILE_EXTENT), // page size, in bytes
MEM_COMMIT, // allocate a committed page
PAGE_READWRITE); // read/write access
m_fc = sFileExtents->sFileExtents;
if(ifcSize > 0)
memcpy(m_fc, fcTemp, (ifcSize+1)*sizeof(FILE_EXTENT));
if(ifcSize > 0)
m_fc += (ifcSize); // must point to first empty array member
iOff = iExtentOffset + iDataRunOffset;
iCnt = 0;
// Do loop to find the FILE_EXTENT and add the details
while(bData[iOff] > 0) // Last Extent will end with 0x00
{
dwHigh = (bData[iOff] & 15 ); // Highbits - Byte Length of run
dwLow = (bData[iOff] & 240); // Low Bits - Byte Length of cluster
dwLow = (dwLow >> 4); // Bitshift 4 to low bits
iOff += (dwHigh + dwLow +1); // will be on the next extent length byte (ie 0x11, 0x31, etc)
iCnt++;
/**********************************************************************************************
Get File Extents
dwLow = cluster offset
MFTReadVar takes the low byte which is the virtual cluster number and
calculates the virtual cluster number.
The bytes are encoded and may be negative, which is why this func is called
**********************************************************************************************/
iTemp = 0;
pdwLow = dwLow;
iTemp = MFTReadVar(&bData[iOff-pdwLow], pdwLow);
// //TRACE1("\nVar Calc Says : %i", iTemp);
/**********************************************************************************************
save the calculated cluster offset
**********************************************************************************************/
if(iCnt < 2) // this is the first record so we save the return value as is - its a logical value
{
m_fc->iFileExtentStartCluster = iTemp;
////TRACE0("\nClusters offsets from start of volume");
////TRACE1("\nStart Cluster : %i", iTemp);
}
else // 2nd or more record, we need to add the previous start cluster as this is a virtual cluster no
{ // (ie offset from the last starting offset/cluster)
m_de = m_fc; // save pointer to previous array - we need the data from this in a few lines
m_fc++;
m_fc->iFileExtentStartCluster = (m_de->iFileExtentStartCluster + iTemp);
////TRACE1("\nStart Cluster : %i", m_fc->iFileExtentStartCluster);
}
/*
Get File/Extent Length (For this Extent Only)
dwHigh = extent length - (extent length is the no. of contigous cluster which make up an extent)
*/
j = dwHigh-1;
iTemp = 0;
for(int i=0;i<(int)dwHigh;i++) // bytes are reversed and encoded
{
k = iOff - dwLow -1; // On To First Length Byte
if(bData[k-i] > 0)
iTemp += bData[k-i] * (DWORD)pow(256, j); // Calc Actual Offset
j--;
}
if(iCnt < 2) // First Record - so add the length of this extent to the starting cluster we saved earlier to get the end cluster
{ // for this extent
m_fc->iFileExtentEndCluster = m_fc->iFileExtentStartCluster + (iTemp-1);
////TRACE1("\nEnd Cluster : %i", m_fc->iFileExtentEndCluster);
}
else
{
m_fc->iFileExtentEndCluster = m_fc->iFileExtentStartCluster + (iTemp-1);
////TRACE1("\nEnd Cluster : %i", m_fc->iFileExtentEndCluster);
}
dwTemp = (bData[17] << 8);
dwTemp = dwTemp + bData[16];
m_fc->iMFTRecordNo = dwTemp;
} // end while
// Get The Real Size Of The Complete File - Reset Offsets
iDataRunOffset = bData[32+iExtentOffset];
iOff = (iExtentOffset + iDataRunOffset) - 16; // This calc took us to the start of the data runs
// if we minus 16 we get the the start of the real file size
m_fc2->iFileLength +=
bData[iOff] +
(bData[iOff+1] * 256) +
(bData[iOff+2] * 65536) +
(bData[iOff+3] * 16777216)+
(bData[iOff+4] * 4294967296)+
(bData[iOff+5] * 1099511627776)+
(bData[iOff+6] * 281474976710656)+
(bData[iOff+7] * 72057594037927936);
////TRACE1("\nFile size is : %i bytes" , m_fc2->iFileLength);
// add last record with '0' to denote the end of file extents
m_fc++;
m_fc->iFileExtentEndCluster = 0;
m_fc->iFileExtentStartCluster = 0;
m_fc->iMFTRecordNo = 0;
return (iCnt + (ifcSize < 0 ? 0 : ifcSize));
}
/*
Fills The Partition Boot Sector Table Structure With Selected Partition Info
NFTS Partition is the record at the start of the volume/partition that holds
info such as sector/cluster size where the 1st MFT is located
OS: Specific To NTFS
Notes: Always a phy disk must be open, the function should be sent the offset
of the partition from the start of the phy disk
*/
BOOL CDiskAnalysis::GetNTFSPartitionTable(int iPartition)
{
DWORD dwErr = 0;
BOOL bSuccess;
DWORD lpdRead;
BYTE *byData2;
byData2 = (BYTE*)VirtualAlloc(
NULL, // next page to commit
512, // page size, in bytes
MEM_COMMIT, // allocate a committed page
PAGE_READWRITE); // read/write access
if (byData2 == NULL )
{
//TRACE0("\nVirtualAlloc failed");
return FALSE;
} else
if(m_HndData == NULL)
{
//TRACE0("GetPartitionTable returning FALSE because file handle was NULL.");
return false;
}
// TODO - Need Check That Master Boot Record Has Been Read ( GetMBR() )
// TODO * 512 (Bytes Per Sector Need Calc not hardset)
LARGE_INTEGER liDist;
liDist.HighPart = 0;
liDist.LowPart = 0;
// if iPartition is <0 (-1) then must be a logical disk open
//__int64 iLge = (iPartition < 0 ? 0 : m_HDPartitionTable[iPartition].dwRelativeSector);
__int64 iLge = (iPartition < 0 ? 0 : 63); // BUG: temp fix while redoing GetMBR
__int64 iRelSector = iLge; //TODO : Needs Tidying ?? maybe
if(iPartition < 0)
ASSERT( (iPartition < 0) && (m_HandleType == OPEN_LOGICAL) );
iLge *= 512;
liDist.QuadPart = iLge;
dwErr = SetFilePointerEx(m_HndData, liDist, NULL, FILE_BEGIN);
if(dwErr == 0)
{
dwErr = GetLastError();
//TRACE1("Failed To Set File Pointer - DiskAnalysis.cpp-GetPartitionTable() Error:%i", dwErr);
return false;
}
// Read In 512 Bytes - (Always the same for all disks)
bSuccess = ReadFile(m_HndData, byData2, 512, &lpdRead, NULL);
if(!bSuccess)
{
dwErr = GetLastError();
//TRACE1("Failed To Get File Data - DiskAnalysis.cpp-GetPartitionTable() Error:%i", dwErr);
return false;
}
// We are now at the Start of Partition i, at the Partition Boot Sector.
// Working From Relative Offsets As We have read in another 512
m_PartitionBootSector.iBytesPerSector = // Offset 0x0B
byData2[11] +
(byData2[12] * 256);
m_PartitionBootSector.iSectorsPerCluster = byData2[13]; // Offset 0x0D
m_PartitionBootSector.iReservedSectors = // Offset 0x0E
byData2[14] +
(byData2[15] * 256);
//m_PartitionBootSector.chReserved = 0; // OffSet 0x10
// // offset 0x13
m_PartitionBootSector.iMediaDesc = byData2[21] ; // offset 0x15
// always 0 // offset 0x16
m_PartitionBootSector.iSectorsPerTrack = // 0x18
byData2[24] +
(byData2[25] * 256);
m_PartitionBootSector.iNumOfHeads = // 0x1a
byData2[26] +
(byData2[27] * 256);
m_PartitionBootSector.iHiddenSectors = // 0x1C
byData2[28] +
(byData2[29] * 256) +
(byData2[30] * 65536) +
(byData2[31] * 16777216);
// 0x20 - Not Used By NTFS 32,33, 34, 35
// 0x24 - Not Used By NTFS 36,37, 38, 39
m_PartitionBootSector.llTotalSectors = // 0x28
byData2[40] +
(byData2[41] * 256) +
(byData2[42] * 65536) +
(byData2[43] * 16777216)+
(byData2[44] * 4294967296)+
(byData2[45] * 1099511627776)+
(byData2[46] * 281474976710656)+
(byData2[47] * 72057594037927936);
m_PartitionBootSector.llLogicalClusterNumForMFT = // $mft sector start
byData2[48] +
(byData2[49] * 256) +
(byData2[50] * 65536) +
(byData2[51] * 16777216)+
(byData2[52] * 4294967296)+
(byData2[53] * 1099511627776)+
(byData2[54] * 281474976710656)+
(byData2[55] * 72057594037927936) + iRelSector;
m_PartitionBootSector.llLogicalClusterNumForMFTMrr = // $mft mirror start location
byData2[56] +
(byData2[57] * 256) +
(byData2[58] * 65536) +
(byData2[59] * 16777216)+
(byData2[60] * 4294967296)+
(byData2[61] * 1099511627776)+
(byData2[62] * 281474976710656)+
(byData2[63] * 72057594037927936) + iRelSector;
m_PartitionBootSector.iClustersPerMFTFileRecord = // 0x40
byData2[64] +
(byData2[65] * 256) +
(byData2[66] * 65536) +
(byData2[67] * 16777216);
m_PartitionBootSector.iClustersPerIndexBlock = // 0x44
byData2[68] +
(byData2[69] * 256) +
(byData2[70] * 65536) +
(byData2[71] * 16777216);
m_PartitionBootSector.llVolumeSerialNumber = (LONGLONG) // 0x48
byData2[72] +
(byData2[73] * 256) +
(byData2[74] * 65536) +
(byData2[75] * 16777216)+
(byData2[76] * 4294967296)+
(byData2[77] * 1099511627776)+
(byData2[78] * 281474976710656)+
(byData2[79] * 72057594037927936);
m_PartitionBootSector.iChecksum = // 0x50
byData2[80] +
(byData2[81] * 256) +
(byData2[82] * 65536) +
(byData2[83] * 16777216);
return GetMFTOffsets();
}
/*
Walks The MFT To Find All Clusters That The MFT Uses
The MFT_CLUSTERS structure will be added to inthe array until no more records need to be added
the last record in this struct will have zeros and will mean that no more data exists.
OS: NT/2000/XP
*/
BOOL CDiskAnalysis::GetMFTOffsets(void)
{
// TODO - Error Checking For NULL FILE
__int64 lMFTSectorBytes = 0;
DWORD dwErr;
DWORD lpdRead;
CString strTest, str;
int iMFTRecSize = 1;
BOOL bSuccess;
// TODO Could have opened another disk/vol/file
// Using the Logical cluster number convert this to bytes for SetFilePointerEx
lMFTSectorBytes = m_PartitionBootSector.llLogicalClusterNumForMFT *
m_PartitionBootSector.iBytesPerSector; // Convert To Bytes
LARGE_INTEGER li;
li.HighPart =0;
li.LowPart = 0;
li.QuadPart = lMFTSectorBytes;
dwErr = SetFilePointerEx(m_HndData, li, NULL , FILE_BEGIN);
if(dwErr == 0)
{
dwErr = GetLastError();
//TRACE1("\nFailed To Set File PointerEX - DiskAnalysis.cpp-GetMFTOffsets() Error:%i", dwErr);
return false;
}
// Calculate Size Of one MFT Record
iMFTRecSize =
( ( this->m_PartitionBootSector.iClustersPerMFTFileRecord
* this->m_PartitionBootSector.iSectorsPerCluster )
* this->m_PartitionBootSector.iBytesPerSector );
BYTE byData2[1007616]; // Temp Storage Of The Record
// Read in one MFT Record (This Being The MFT record itself)
bSuccess = ReadFile(m_HndData, byData2, iMFTRecSize, &lpdRead, NULL); //used to be iMFTRecSize not 1024
if(!bSuccess)
{
dwErr = GetLastError();
TRACE1("\nFailed To Get File Data - DiskAnalysis.cpp-GetPartitionTable() Error:%i", dwErr);
return false;
}
// As we need the value of Attribute ($DATA) we need to open the $AttrDef File to get
// the value of $DATA - currently 0x80
// CString strDataDef = "$DATA";
// DWORD dwAttrDef = GetAttrDef(strDataDef);
// Get The First Attribute Offset
DWORD iOff = byData2[20];
// We want to find attibute 0x80 which is the data attribute which has
// the extents.
DWORD dwAttrib;
DWORD dwLength;
DWORD dwOffsetSave, pdwLow = 0;;
for(;;)
{
// Go To First Attribute
dwAttrib = byData2[iOff];
// What Attribute Have We Found.
if(dwAttrib == 0x80) // If data attribute
{
dwOffsetSave = iOff; // Save Offset for later use
int iDataRunOffset = byData2[32+iOff]; // iDataRunOffset will now have the offset of the start of the DRuns
// Offset From Attribute Header 0x80 $DATA.
// iOff has the Attribute Header (0x80 $DATA)Offset position
int dwData = iOff + iDataRunOffset; // Take the Start of the Attribute Header (0x80 $ DATA) and add the offset
// value of the data runs from the header and we end up with the
// position of the start of the extent runs in the MFT file.
// Offset to the start of the data runs from the attribute header
__int64 dwCalc =0, dwCalc2 =0, dwOffset =0;
DWORD dwHigh =0, dwLow =0;
int j=0,i=0, ks=0;
MFT_CLUSTERS *m_Ca, *m_Cab;
while(byData2[dwData] > 0)
{
dwHigh = (byData2[dwData] & 15 ); // Highbits - Byte Length of run
dwLow = (byData2[dwData] & 240); // Low Bits - Byte Length of cluster
dwLow = (dwLow >> 4);
dwData += (dwHigh + dwLow + 1);
//*******************************************************************************************************************************//
// EXTENT LENGTH
// Get Length Of Run For This Extent we need to get the next dwHigh bytes
// dwHigh = Length Of Extent Run for length of this extent data
//*******************************************************************************************************************************//
//dwCalc2 = 0;
//dwOffset = iOff + dwData + dwHigh; // On Last Byte Now
j = dwHigh-1;
dwCalc2 = 0;
for(i=0;i<=(int)dwHigh;i++)
{
dwOffset = dwData - dwLow -1;
if(byData2[dwOffset-i] > 0)
dwCalc2 += byData2[dwOffset-i] * (DWORD)pow(256, j);
j--;
}
////TRACE1("\nLength Of Extent Run (In This Case Length Of This Section Of MFT) : 0x%X", dwCalc2 );
//*******************************************************************************************************************************//
// EXTENT STARTING OFFSET
// Get Cluster Offset Value
// dwLow = Length Of Extent Run for cluster value offset
//*******************************************************************************************************************************//
pdwLow = dwLow;
dwCalc = MFTReadVar(&byData2[dwData-pdwLow], pdwLow);
// Add To Struct Array
// Reallocate Storage to fit new record
ks++;
m_ClusterArray = (MFT_CLUSTERS*)realloc(m_ClusterArray, ks * sizeof(MFT_CLUSTERS) );
if(ks > 1) // Second or more record
{
m_Ca = m_ClusterArray; // Get New Starting Address For Bigger Array
m_Ca = m_Ca + (ks-1); // Move Pointer To Next New Record (0 Based Index (ks-1), ks start at 1 )
m_Cab = m_ClusterArray; // Get New Address For Bigger Array New Record
m_Cab = (m_Ca-1); // Set To last added record to get last details
m_Ca->iStartCluster = m_Cab->iStartCluster + (__int64)dwCalc; // Fill In New Record
m_Ca->iEndCluster = m_Ca->iStartCluster + (__int64)dwCalc2; // Fill In New Record
m_Ca->iMFTRecSize = ((m_Ca->iEndCluster - m_Ca->iStartCluster)
/ this->m_PartitionBootSector.iClustersPerMFTFileRecord );
}
else // Must Be First Record (Just Add The Start and len to get end cluster)
{
m_Ca = m_ClusterArray; // Point To First Record
m_Ca->iStartCluster = (__int64)dwCalc;
m_Ca->iEndCluster = (__int64)dwCalc2 + dwCalc;
m_Ca->iMFTRecSize = ( (m_Ca->iEndCluster - m_Ca->iStartCluster )
/ this->m_PartitionBootSector.iClustersPerMFTFileRecord );
}
}// end while(dwData > 0)
// Allocate Storage - Create One More Record With 0 to show end of data
ks++;
m_ClusterArray = (MFT_CLUSTERS*)realloc(m_ClusterArray, ks * sizeof(MFT_CLUSTERS) );
m_Ca = m_ClusterArray;
m_Ca += (ks-1);