From edd737cd17f4e6712ba289e977c330ca3adc5495 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 17 Sep 2003 19:37:33 +0000 Subject: Initial Import --- .cvsignore | 3 + src/StdAfx.cpp | 29 +++ src/StdAfx.h | 44 ++++ src/drive.cpp | 137 ++++++++++++ src/drive.h | 93 ++++++++ src/locks.h | 32 +++ src/memref.h | 49 +++++ src/misc.c | 107 +++++++++ src/ntfs.c | 122 +++++++++++ src/ntfs.h | 203 ++++++++++++++++++ src/ntfsx.c | 259 ++++++++++++++++++++++ src/ntfsx.h | 114 ++++++++++ src/scrounge.c | 654 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/scrounge.h | 33 +++ src/usuals.h | 45 ++++ win32/win32.dsp | 147 +++++++++++++ win32/win32.dsw | 29 +++ 17 files changed, 2100 insertions(+) create mode 100644 .cvsignore create mode 100644 src/StdAfx.cpp create mode 100644 src/StdAfx.h create mode 100644 src/drive.cpp create mode 100644 src/drive.h create mode 100644 src/locks.h create mode 100644 src/memref.h create mode 100644 src/misc.c create mode 100644 src/ntfs.c create mode 100644 src/ntfs.h create mode 100644 src/ntfsx.c create mode 100644 src/ntfsx.h create mode 100644 src/scrounge.c create mode 100644 src/scrounge.h create mode 100644 src/usuals.h create mode 100644 win32/win32.dsp create mode 100644 win32/win32.dsw diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..a243ba0 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,3 @@ +Debug +Release +Trash \ No newline at end of file diff --git a/src/StdAfx.cpp b/src/StdAfx.cpp new file mode 100644 index 0000000..d939e46 --- /dev/null +++ b/src/StdAfx.cpp @@ -0,0 +1,29 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + + +// stdafx.cpp : source file that includes just the standard includes +// Scrounge.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + diff --git a/src/StdAfx.h b/src/StdAfx.h new file mode 100644 index 0000000..38f0a95 --- /dev/null +++ b/src/StdAfx.h @@ -0,0 +1,44 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__552C4258_1192_4B38_948A_887884424C46__INCLUDED_) +#define AFX_STDAFX_H__552C4258_1192_4B38_948A_887884424C46__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include +#include +#include +#include "memref.h" + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__552C4258_1192_4B38_948A_887884424C46__INCLUDED_) diff --git a/src/drive.cpp b/src/drive.cpp new file mode 100644 index 0000000..f485cf6 --- /dev/null +++ b/src/drive.cpp @@ -0,0 +1,137 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + + +#include "drive.h" +#include "memref.h" +#include +#include +#include + + +PartitionInfo* CreatePartitionInfo() +{ + PartitionInfo* pInfo = (PartitionInfo*)refalloc(sizeof(PartitionInfo)); + if(pInfo) + memset(pInfo, 0, sizeof(PartitionInfo)); + + return pInfo; +} + +void FreePartitionInfo(PartitionInfo* pInfo) +{ + if(pInfo) + { + if(pInfo->pLocks) + free(pInfo->pLocks); + + refrelease(pInfo); + } +} + +// These locks are used to signify which +struct DriveLock +{ + uint64 beg; + uint64 end; +}; + + +bool IntersectRange(uint64& b1, uint64& e1, uint64 b2, uint64 e2) +{ + // TODO: (later) This is stupid! There's a simple quick way + // need to update + + if(b1 <= b2 && e1 > b2 && b1 < e2) // Overlaps the first portion + e1 = e2; + else if(b1 < e2 && e1 >= e2 && b2 < e1) // Overlaps second portion + b1 = b2; + else if(b1 > b2 && e1 < e2) // Encompassed + { } + else if (b1 == b2 && e1 == e2) // Identical + { } + else + return false; + + return true; +} + +void AddLocationLock(PartitionInfo* pInfo, uint64 beg, uint64 end) +{ + if(pInfo->cLocks <= pInfo->curLock) + { + pInfo->cLocks += 0x400; + pInfo->pLocks = (DriveLock*)realloc(pInfo->pLocks, sizeof(DriveLock) * pInfo->cLocks); + } + + // TODO: Implement a more efficient method here! + + if(pInfo->pLocks) + { + bool bFound = false; + + // Go through and check for a current lock we can tag onto + for(uint32 i = 0; i < pInfo->curLock; i++) + { + if(IntersectRange(pInfo->pLocks[i].beg, pInfo->pLocks[i].end, + beg, end)) + { + bFound = true; + } + } + + if(!bFound) + { + pInfo->pLocks[pInfo->curLock].beg = beg; + pInfo->pLocks[pInfo->curLock].end = end; + pInfo->curLock++; + } + } +} + +bool CheckLocationLock(PartitionInfo* pInfo, uint64& sec) +{ + // Go through and check for a lock + for(uint32 i = 0; i < pInfo->curLock; i++) + { + if(sec >= pInfo->pLocks[i].beg && + sec < pInfo->pLocks[i].end) + { + sec = pInfo->pLocks[i].end; + return true; + } + } + + return false; +} + +#ifdef _DEBUG +void DumpLocationLocks(PartitionInfo* pInfo) +{ + for(uint32 i = 0; i < pInfo->curLock; i++) + { + printf("%u\t%u\n", (uint32)pInfo->pLocks[i].beg, (uint32)pInfo->pLocks[i].end); + } + + printf("\n"); +} +#endif diff --git a/src/drive.h b/src/drive.h new file mode 100644 index 0000000..bec704e --- /dev/null +++ b/src/drive.h @@ -0,0 +1,93 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + + +#ifndef __DRIVE_H__20010822 +#define __DRIVE_H__20010822 + +#include "usuals.h" + +const uint16 kSectorSize = 0x200; +struct DriveLock; + +struct PartitionInfo +{ + uint32 firstSector; + uint32 lastSector; + uint32 offMFT; // In sectors + byte clusterSize; // In sectors + + DriveLock* pLocks; + uint32 cLocks; + uint32 curLock; +}; + +PartitionInfo* CreatePartitionInfo(); +void FreePartitionInfo(PartitionInfo* pInfo); + + +#pragma pack(push, drive) +#pragma pack(1) + +const byte kPartition_Invalid = 0; +const byte kPartition_Extended = 5; +const byte kPartition_ExtendedLBA = 15; + + +// Partition table entry +struct Drive_PartEntry +{ + byte active; // partition bootable flag + byte starthead; // starting head + byte startsector; // starting sector and 2 MS bits of cylinder + byte startcylinder; // starting cylinder (low 8 bits) + byte system; // partition type + byte endhead; // ending head + byte endsector; // ending sector and 2 MS bits of cylinder + byte endcylinder; // ending cylinder (low 8 bits) + uint32 startsec; // absolute starting sector + uint32 endsec; // absolute ending sector +}; + +const uint16 kMBR_Sig = 0xAA55; + +// Master Boot Record +struct Drive_MBR +{ + byte fill[0x1be]; // boot code + Drive_PartEntry partitions[4]; // partition table + uint16 sig; // 55AAh boot signature +}; + +#pragma pack(pop, drive) + +#define CLUSTER_TO_SECTOR(info, clus) (((clus) * (info).clusterSize) + (info).firstSector) +#define SECTOR_TO_BYTES(sec) ((sec) * kSectorSize) +#define CLUSTER_SIZE(info) ((info).clusterSize * kSectorSize) + +bool IntersectRange(uint64& b1, uint64& e1, uint64 b2, uint64 e2); +void AddLocationLock(PartitionInfo* pInfo, uint64 beg, uint64 end); +bool CheckLocationLock(PartitionInfo* pInfo, uint64& sec); + + + +#endif //__DRIVE_H__20010822 \ No newline at end of file diff --git a/src/locks.h b/src/locks.h new file mode 100644 index 0000000..d60878c --- /dev/null +++ b/src/locks.h @@ -0,0 +1,32 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + +#ifndef __LOCKS_H__20010828 +#define __LOCKS_H__20010828 + + +#include "drive.h" + + + + +#endif //__LOCKS_H__20010828 \ No newline at end of file diff --git a/src/memref.h b/src/memref.h new file mode 100644 index 0000000..a26ac34 --- /dev/null +++ b/src/memref.h @@ -0,0 +1,49 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + + +#ifndef __MEMREF_H__20010827 +#define __MEMREF_H__20010827 + +#ifdef _DEBUG + +void* _refalloc_dbg(size_t sz); +void* _refadd_dbg(void* pBuff); +void _refrelease_dbg(void* pBuff); + +#define refalloc _refalloc_dbg +#define refadd _refadd_dbg +#define refrelease _refrelease_dbg + +#else + +void* _refalloc(size_t sz); +void* _refadd(void* pBuff); +void _refrelease(void* pBuff); + +#define refalloc _refalloc +#define refadd _refadd +#define refrelease _refrelease + +#endif + +#endif //__MEMREF_H__20010827 \ No newline at end of file diff --git a/src/misc.c b/src/misc.c new file mode 100644 index 0000000..5ea67cd --- /dev/null +++ b/src/misc.c @@ -0,0 +1,107 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + + +#include "memref.h" +#include "malloc.h" +#include "assert.h" + +// WARNING!! Not thread safe or very efficient for large +// amounts of memory allocations + +const size_t kRefSig = 0x1F2F3F4F; + +void* _refalloc_dbg(size_t sz) +{ + // Allocate extra counter value before memory + size_t* pMem = (size_t*)malloc(sz * sizeof(size_t) * 2); + + if(pMem) + { + pMem[0] = kRefSig; + pMem[1] = 1; + return pMem + 2; + } + + return pMem; +} + +void* _refalloc(size_t sz) +{ + // Allocate extra counter value before memory + size_t* pMem = (size_t*)malloc(sz * sizeof(size_t) * 1); + + if(pMem) + { + pMem[0] = 1; + return pMem + 1; + } + + return pMem; +} + +void* _refadd_dbg(void* pBuff) +{ + if(pBuff) + { + // Increment the counter value + size_t* pMem = (size_t*)pBuff - 2; + assert(pMem[0] = kRefSig); + pMem[1]++; + } + + return pBuff; +} + +void* _refadd(void* pBuff) +{ + if(pBuff) + // Increment the counter value + ((size_t*)pBuff)[-1]++; + + return pBuff; +} + +void _refrelease_dbg(void* pBuff) +{ + if(pBuff) + { + // Decrement the counter value + size_t* pMem = (size_t*)pBuff - 2; + assert(pMem[0] = kRefSig); + + if(!--pMem[1]) + free(pMem); + } +} + +void _refrelease(void* pBuff) +{ + if(pBuff) + { + // Decrement the counter value + size_t* pMem = (size_t*)pBuff - 1; + + if(!--pMem[0]) + free(pMem); + } +} \ No newline at end of file diff --git a/src/ntfs.c b/src/ntfs.c new file mode 100644 index 0000000..28b4cca --- /dev/null +++ b/src/ntfs.c @@ -0,0 +1,122 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + + +#include "usuals.h" +#include "ntfs.h" + +#include "malloc.h" +#include "string.h" + + +NTFS_AttribHeader* NTFS_SearchAttribute(byte* pLocation, uint32 attrType, void* pEnd, bool bSkip) +{ + // Now we should be at attributes + while((!pEnd || (pLocation + sizeof(NTFS_AttribHeader) < pEnd)) && + *((uint32*)pLocation) != kNTFS_RecEnd) + { + NTFS_AttribHeader* pAttrib = (NTFS_AttribHeader*)pLocation; + + if(!bSkip) + { + if(pAttrib->type == attrType) + return pAttrib; + } + else + bSkip = false; + + pLocation += pAttrib->cbAttribute; + } + + return NULL; +} + + +NTFS_AttribHeader* NTFS_FindAttribute(NTFS_RecordHeader* pRecord, uint32 attrType, void* pEnd) +{ + byte* pLocation = (byte*)pRecord; + pLocation += kNTFS_RecHeaderLen; + + return NTFS_SearchAttribute(pLocation, attrType, pEnd, false); +} + +NTFS_AttribHeader* NTFS_NextAttribute(NTFS_AttribHeader* pAttrib, uint32 attrType, void* pEnd) +{ + return NTFS_SearchAttribute((byte*)pAttrib, attrType, pEnd, true); +} + +void* NTFS_GetAttributeData(NTFS_AttribResident* pAttrib, void* pEnd) +{ + void* pData = ((byte*)pAttrib) + pAttrib->offAttribData; + if(!pEnd && pData > pEnd) + return NULL; + + return pData; +} + +bool NTFS_IsBetterNameSpace(byte n1, byte n2) +{ + // We like our namespaces in this order + // 1. WIN32 + // 2. WIN32/DOS + // 3. DOS + // 4. POSIX + + if(n1 == kNTFS_NameSpacePOSIX) + return true; + if(n1 == kNTFS_NameSpaceDOS && + (n2 == kNTFS_NameSpaceWIN32 || n2 == kNTFS_NameSpaceWINDOS)) + return true; + if(n1 == kNTFS_NameSpaceWINDOS && + n2 == kNTFS_NameSpaceWIN32) + return true; + + return false; +} + +bool NTFS_DoFixups(byte* pCluster, uint32 cbCluster) +{ + ASSERT(cbCluster % kSectorSize == 0); + NTFS_RecordHeader* pRecord = (NTFS_RecordHeader*)pCluster; + + byte numSectors = (byte)(cbCluster / kSectorSize); + + // Check the number of sectors against array + if(pRecord->cwUpdSeq - 1 < numSectors) + numSectors = pRecord->cwUpdSeq - 1; + + uint16* pUpdSeq = (uint16*)(pCluster + pRecord->offUpdSeq); + + for(byte i = 0; i < numSectors; i++) + { + // Check last 2 bytes in each sector against + // first double byte value in update sequence + uint16* pSectorFooter = (uint16*)((pCluster + (kSectorSize - 2)) + (i * kSectorSize)); + if(*pSectorFooter == pUpdSeq[0]) + *pSectorFooter = pUpdSeq[i + 1]; + else + return false; + } + + return true; +} + diff --git a/src/ntfs.h b/src/ntfs.h new file mode 100644 index 0000000..59a129b --- /dev/null +++ b/src/ntfs.h @@ -0,0 +1,203 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + +#ifndef __NTFS_H__ +#define __NTFS_H__ + +#include "usuals.h" +#include "stddef.h" +#include "drive.h" + +#pragma pack(push, ntfs) +#pragma pack(1) + + +// WARNING Assumptions: +const uint32 kNTFS_RecordLen = 0x0400; + + +const char kNTFS_SysId[] = "NTFS "; + +struct NTFS_BootSector +{ + byte jmp[3]; // Jump to the boot loader routine + char sysId[8]; // System Id: "NTFS " + uint16 bytePerSec; // Bytes per sector + byte secPerClus; // Sectors per cluster + byte padding[7]; // Unused + byte mediaDescriptor; // Media descriptor (a) + byte padding2[2]; // Unused + uint16 secPerTrack; // Sectors per track + uint16 numHeads; // Number of heads + byte padding3[8]; // Unused + uint32 signature; // Always 80 00 80 00 + uint64 cSectors; // Number of sectors in the volume + uint64 offMFT; // LCN of VCN 0 of the $MFT + uint64 offMFTMirr; // LCN of VCN 0 of the $MFTMirr + uint32 clusPerMFT; // Clusters per MFT Record (b) + uint32 clusPerIndex; // Clusters per Index Record + uint64 serialNum; // Volume serial number +}; + +const uint32 kNTFS_RecMagic = 'ELIF'; +const uint32 kNTFS_RecEnd = 0xFFFFFFFF; +const uint32 kNTFS_RecHeaderLen = 0x30; + +const uint16 kNTFS_RecFlagUse = 0x01; +const uint16 kNTFS_RecFlagDir = 0x02; + +struct NTFS_RecordHeader +{ + uint32 magic; // Magic number 'FILE' + uint16 offUpdSeq; // Offset to the update sequence + uint16 cwUpdSeq; // Size in words of Update Sequence Number & Array (S) + uint64 logSeqNum; // $LogFile Sequence Number (LSN) + uint16 seqNum; // Sequence number + uint16 cHardlinks; // Hard link count + uint16 x_offUpdSeqArr; // Offset to Update Sequence Array (DON'T THINK SO) + uint16 flags; // Flags + uint32 cbRecord; // Real size of the FILE record + uint32 cbAllocated; // Allocated size of the FILE record + uint64 refBaseRecord; // File reference to the base FILE record + uint16 nextAttrId; // Next Attribute Id + uint16 padding; // (XP) Align to 4 byte boundary + uint32 recordNum; // (XP) Number of this MFT Record +}; + + + +const uint16 kNTFS_AttrCompressed = 0x0001; +const uint16 kNTFS_AttrEncrypted = 0x0002; +const uint16 kNTFS_AttrSparse = 0x0004; + +struct NTFS_AttribHeader +{ + uint32 type; // Attribute Type (e.g. 0x10, 0x60) + uint32 cbAttribute; // Length (including this header) + byte bNonResident; // Non-resident flag + byte cName; // Name length + uint16 offName; // Offset to the Attribute + uint16 flags; // Flags + uint16 idAttribute; // Attribute Id (a) +}; + +struct NTFS_AttribResident +{ + NTFS_AttribHeader header; + + uint32 cbAttribData; // Length of the Attribute + uint16 offAttribData; // Offset to the Attribute + byte bIndexed; // Indexed flag + byte padding; // 0x00 Padding +}; + +struct NTFS_AttribNonResident +{ + NTFS_AttribHeader header; + + uint64 startVCN; // Starting VCN + uint64 lastVCN; // Last VCN + uint16 offDataRuns; // Offset to the Data Runs + uint16 compUnitSize; // Compression Unit Size (b) + uint32 padding; // Padding + uint64 cbAllocated; // Allocated size of the attribute (c) + uint64 cbAttribData; // Real size of the attribute + uint64 cbInitData; // Initialized data size of the stream (d) +}; + + +const uint32 kNTFS_ATTRIBUTE_LIST = 0x20; +const uint32 kNTFS_FILENAME = 0x30; +const uint32 kNTFS_DATA = 0x80; + + +const uint32 kNTFS_FileReadOnly = 0x0001; +const uint32 kNTFS_FileHidden = 0x0002; +const uint32 kNTFS_FileSystem = 0x0004; +const uint32 kNTFS_FileArchive = 0x0020; +const uint32 kNTFS_FileDevice = 0x0040; +const uint32 kNTFS_FileNormal = 0x0080; +const uint32 kNTFS_FileTemorary = 0x0100; +const uint32 kNTFS_FileSparse = 0x0200; +const uint32 kNTFS_FileReparse = 0x0400; +const uint32 kNTFS_FileCompressed = 0x0800; +const uint32 kNTFS_FileOffline = 0x1000; +const uint32 kNTFS_FileNotIndexed = 0x2000; +const uint32 kNTFS_FileEncrypted = 0x4000; + +const byte kNTFS_NameSpacePOSIX = 0x00; +const byte kNTFS_NameSpaceWIN32 = 0x01; +const byte kNTFS_NameSpaceDOS = 0x02; +const byte kNTFS_NameSpaceWINDOS = 0x03; + +const wchar_t kNTFS_MFTName[] = L"$MFT"; + +struct NTFS_AttrFileName +{ + uint64 refParent; // File reference to the parent directory. + uint64 timeCreated; // C Time - File Creation + uint64 timeAltered; // A Time - File Altered + uint64 timeModified; // M Time - MFT Changed + uint64 timeRead; // R Time - File Read + uint64 cbAllocated; // Allocated size of the file + uint64 cbFileSize; // Real size of the file + uint32 flags; // Flags, e.g. Directory, compressed, hidden + uint32 eaReparse; // Used by EAs and Reparse + byte cFileName; // Filename length in characters (L) + byte nameSpace; // Filename namespace + // File Name comes here +}; + +struct NTFS_AttrListRecord +{ + uint32 type; // Type + uint16 cbRecord; // Record length + byte cName; // Name length (N) + byte offName; // Offset to Name (a) + uint64 startVCN; // Starting VCN (b) + uint64 refAttrib; // Base File Reference of the attribute + uint16 idAttribute; // Attribute Id (c) + // Attribute name here +}; + +#pragma pack(pop, ntfs) + +NTFS_AttribHeader* NTFS_FindAttribute(NTFS_RecordHeader* pRecord, uint32 attrType, void* pEnd); +NTFS_AttribHeader* NTFS_NextAttribute(NTFS_AttribHeader* pAttrib, uint32 attrType, void* pEnd); + +void* NTFS_GetAttributeData(NTFS_AttribResident* pAttrib, void* pEnd); +bool NTFS_IsBetterNameSpace(byte n1, byte n2); +bool NTFS_DoFixups(byte* pCluster, uint32 cbCluster); + + +#define NTFS_RefToSector(info, ref) (((ref & 0xFFFFFFFFFFFF) * (kNTFS_RecordLen / kSectorSize)) + (info).firstSector + (info).offMFT) +#define NTFS_MakeFileTime(i64, ft) ((ft).dwLowDateTime = LOWDWORD(i64), (ft).dwHighDateTime = HIGHDWORD(i64)) + + + +// +// The record, attribute or whatever was invalid on disk +// +#define ERROR_NTFS_INVALID 10801L +#define ERROR_NTFS_NOTIMPLEMENT 10802L + +#endif //__NTFS_H__ \ No newline at end of file diff --git a/src/ntfsx.c b/src/ntfsx.c new file mode 100644 index 0000000..fe7d898 --- /dev/null +++ b/src/ntfsx.c @@ -0,0 +1,259 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + +// ntfsx.cpp: implementation of the NTFS_Record class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ntfs.h" +#include "ntfsx.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +bool NTFS_Cluster::New(PartitionInfo* pInfo) +{ + Free(); + + m_cbCluster = CLUSTER_SIZE(*pInfo); + + m_pCluster = (byte*)refalloc(m_cbCluster); + return m_pCluster != NULL; +} + +bool NTFS_Cluster::Read(PartitionInfo* pInfo, uint64 begSector, HANDLE hIn) +{ + if(!m_pCluster) + { + if(!New(pInfo)) + return false; + } + + // Read the mftRecord + uint64 offRecord = SECTOR_TO_BYTES(begSector); + LONG lHigh = HIGHDWORD(offRecord); + if(SetFilePointer(hIn, LOWDWORD(offRecord), &lHigh, FILE_BEGIN) == -1 + && GetLastError() != NO_ERROR) + { + Free(); + return false; + } + + DWORD dwRead = 0; + if(!ReadFile(hIn, m_pCluster, m_cbCluster, &dwRead, NULL) || + dwRead != m_cbCluster) + { + Free(); + ::SetLastError(ERROR_READ_FAULT); + return false; + } + + ::SetLastError(ERROR_SUCCESS); + return true; +} + +NTFS_Record::NTFS_Record(PartitionInfo* pInfo) +{ + m_pInfo = (PartitionInfo*)refadd(pInfo); +} + +NTFS_Record::~NTFS_Record() +{ + refrelease(m_pInfo); +} + +bool NTFS_Record::Read(uint64 begSector, HANDLE hIn) +{ + if(!NTFS_Cluster::Read(m_pInfo, begSector, hIn)) + return false; + + // Check and validate this record + NTFS_RecordHeader* pRecord = GetHeader(); + if(pRecord->magic != kNTFS_RecMagic || + !NTFS_DoFixups(m_pCluster, m_cbCluster)) + { + NTFS_Cluster::Free(); + ::SetLastError(ERROR_NTFS_INVALID); + return false; + } + + return true; +} + +NTFS_Attribute* NTFS_Record::FindAttribute(uint32 attrType, HANDLE hIn) +{ + NTFS_Attribute* pAttribute = NULL; + + // Make sure we have a valid record + ASSERT(GetHeader()); + NTFS_AttribHeader* pHeader = NTFS_FindAttribute(GetHeader(), attrType, (m_pCluster + m_cbCluster)); + + if(pHeader) + { + pAttribute = new NTFS_Attribute(*this, pHeader); + } + else + { + // Do attribute list thing here! + pHeader = NTFS_FindAttribute(GetHeader(), kNTFS_ATTRIBUTE_LIST, (m_pCluster + m_cbCluster)); + + // For now we only support Resident Attribute lists + if(hIn && pHeader && !pHeader->bNonResident) + { + NTFS_AttribResident* pResident = (NTFS_AttribResident*)pHeader; + NTFS_AttrListRecord* pAttr = (NTFS_AttrListRecord*)((byte*)pHeader + pResident->offAttribData); + + // Go through AttrList records looking for this attribute + while((byte*)pAttr < (byte*)pHeader + pHeader->cbAttribute) + { + // Found it! + if(pAttr->type == attrType) + { + // Read in appropriate cluster + uint64 mftRecord = NTFS_RefToSector(*m_pInfo, pAttr->refAttrib); + + NTFS_Record recAttr(m_pInfo); + if(recAttr.Read(mftRecord, hIn)) + { + pAttribute = recAttr.FindAttribute(attrType, hIn); + break; + } + + } + + pAttr = (NTFS_AttrListRecord*)((byte*)pAttr + pAttr->cbRecord); + } + } + } + + return pAttribute; +} + +bool NTFS_Attribute::NextAttribute(uint32 attrType) +{ + NTFS_AttribHeader* pHeader = NTFS_NextAttribute(GetHeader(), attrType, m_pMem + m_cbMem); + if(pHeader) + { + m_pHeader = pHeader; + return true; + } + + return false; +} + +NTFS_Attribute::NTFS_Attribute(NTFS_Cluster& clus, NTFS_AttribHeader* pHeader) +{ + m_pHeader = pHeader; + m_pMem = clus.m_pCluster; + m_cbMem = clus.m_cbCluster; + refadd(m_pMem); +} + +void* NTFS_Attribute::GetResidentData() +{ + ASSERT(!m_pHeader->bNonResident); + NTFS_AttribResident* pRes = (NTFS_AttribResident*)m_pHeader; + return (byte*)m_pHeader + pRes->offAttribData; +} + +uint32 NTFS_Attribute::GetResidentSize() +{ + ASSERT(!m_pHeader->bNonResident); + NTFS_AttribResident* pRes = (NTFS_AttribResident*)m_pHeader; + return pRes->cbAttribData; +} + +NTFS_DataRun* NTFS_Attribute::GetDataRun() +{ + ASSERT(m_pHeader->bNonResident); + NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)m_pHeader; + + return new NTFS_DataRun(m_pMem, (byte*)m_pHeader + pNonRes->offDataRuns); +} + +NTFS_DataRun::NTFS_DataRun(byte* pClus, byte* pDataRun) +{ + ASSERT(pDataRun); + m_pMem = (byte*)refadd(pClus); + m_pDataRun = pDataRun; + m_pCurPos = NULL; + m_firstCluster = m_numClusters = 0; + m_bSparse = false; +} + +bool NTFS_DataRun::First() +{ + m_pCurPos = m_pDataRun; + m_firstCluster = m_numClusters = 0; + m_bSparse = false; + return Next(); +} + +bool NTFS_DataRun::Next() +{ + ASSERT(m_pCurPos); + + if(!*m_pCurPos) + return false; + + byte cbLen = *m_pCurPos & 0x0F; + byte cbOff = *m_pCurPos >> 4; + + // ASSUMPTION length and offset are less 64 bit numbers + if(cbLen == 0 || cbLen > 8 || cbOff > 8) + return false; + + ASSERT(cbLen <= 8); + ASSERT(cbOff <= 8); + + m_pCurPos++; + + memset(&m_numClusters, 0, sizeof(uint64)); + + memcpy(&m_numClusters, m_pCurPos, cbLen); + m_pCurPos += cbLen; + + int64 offset; + + // Note that offset can be negative + if(*(m_pCurPos + (cbOff - 1)) & 0x80) + memset(&offset, ~0, sizeof(int64)); + else + memset(&offset, 0, sizeof(int64)); + + memcpy(&offset, m_pCurPos, cbOff); + m_pCurPos += cbOff; + + if(offset == 0) + { + m_bSparse = true; + } + else + { + m_bSparse = false; + m_firstCluster += offset; + } + + return true; +} diff --git a/src/ntfsx.h b/src/ntfsx.h new file mode 100644 index 0000000..586b36b --- /dev/null +++ b/src/ntfsx.h @@ -0,0 +1,114 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + + +// ntfsx +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_NTFSX__9363C7D2_D3CC_4D49_BEE0_27AD025670F2__INCLUDED_) +#define AFX_NTFSX__9363C7D2_D3CC_4D49_BEE0_27AD025670F2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "ntfs.h" + +class NTFS_DataRun +{ +public: + NTFS_DataRun(byte* pClus, byte* pDataRun); + ~NTFS_DataRun() + { refrelease(m_pMem); } + + bool First(); + bool Next(); + + uint64 m_firstCluster; + uint64 m_numClusters; + bool m_bSparse; + +protected: + byte* m_pMem; + byte* m_pDataRun; + byte* m_pCurPos; +}; + +class NTFS_Cluster +{ +public: + NTFS_Cluster() + { m_pCluster = NULL; }; + ~NTFS_Cluster() + { Free(); } + + bool New(PartitionInfo* pInfo); + bool Read(PartitionInfo* pInfo, uint64 begSector, HANDLE hIn); + void Free() + { if(m_pCluster) refrelease(m_pCluster); } + + uint32 m_cbCluster; + byte* m_pCluster; +}; + +class NTFS_Attribute +{ +public: + NTFS_Attribute(NTFS_Cluster& clus, NTFS_AttribHeader* pHeader); + ~NTFS_Attribute() + { refrelease(m_pMem); } + + NTFS_AttribHeader* GetHeader() + { return m_pHeader; } + + void* GetResidentData(); + uint32 GetResidentSize(); + + NTFS_DataRun* GetDataRun(); + + bool NextAttribute(uint32 attrType); + + +protected: + NTFS_AttribHeader* m_pHeader; + byte* m_pMem; + uint32 m_cbMem; +}; + +class NTFS_Record : public NTFS_Cluster +{ +public: + NTFS_Record(PartitionInfo* pInfo); + ~NTFS_Record(); + + bool Read(uint64 begSector, HANDLE hIn); + NTFS_RecordHeader* GetHeader() + { return (NTFS_RecordHeader*)m_pCluster; } + + NTFS_Attribute* FindAttribute(uint32 attrType, HANDLE hIn); + +protected: + PartitionInfo* m_pInfo; +}; + +#endif // !defined(AFX_NTFSX__9363C7D2_D3CC_4D49_BEE0_27AD025670F2__INCLUDED_) diff --git a/src/scrounge.c b/src/scrounge.c new file mode 100644 index 0000000..8841aa9 --- /dev/null +++ b/src/scrounge.c @@ -0,0 +1,654 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + +// Scrounge.cpp +// + +#include "stdafx.h" +#include "ntfs.h" +#include "ntfsx.h" +#include "usuals.h" +#include "drive.h" +#include "locks.h" +#include "scrounge.h" + +#define RET_ERROR(l) { ::SetLastError(l); bRet = TRUE; goto clean_up; } +#define PASS_ERROR() {bRet = TRUE; goto clean_up; } +#define RET_FATAL(l) { ::SetLastError(l); bRet = FALSE; goto clean_up; } +#define PASS_FATAL() {bRet = FALSE; goto clean_up; } + + +// ---------------------------------------------------------------------- +// Process a potential MFT Record. For directories create the directory +// and for files write out the file. +// +// Current Directory is the output directory +// hIn is an open drive handle +// pInfo is partition info about hIn (needs to be a ref counted pointer) + +BOOL ProcessMFTRecord(PartitionInfo* pInfo, uint64 mftRecord, HANDLE hIn) +{ + // Declare data that needs cleaning up first + BOOL bRet = TRUE; // Return value + HANDLE hFile = NULL; // Output file handle + NTFS_Attribute* pAttribName = NULL; // Filename Attribute + NTFS_Attribute* pAttribData = NULL; // Data Attribute + NTFS_DataRun* pDataRun = NULL; // Data runs for nonresident data + + + // Tracks whether or not we output a file + bool bFile = false; + + { + + // Read the MFT record + NTFS_Record record(pInfo); + if(!record.Read(mftRecord, hIn)) + PASS_ERROR(); + + NTFS_RecordHeader* pRecord = record.GetHeader(); + + + // Check if this record is in use + if(!(pRecord->flags & kNTFS_RecFlagUse)) + RET_ERROR(ERROR_SUCCESS); + + + // Info that we use later + WCHAR fileName[MAX_PATH + 1]; + FILETIME ftCreated; + FILETIME ftModified; + FILETIME ftAccessed; + DWORD fileAttrib = 0; + uint64 mftParent = 0; + byte* pResidentData = NULL; + + + // Now get the name and info... + pAttribName = record.FindAttribute(kNTFS_FILENAME, hIn); + if(!pAttribName) RET_ERROR(ERROR_SUCCESS); + + + byte nameSpace = kNTFS_NameSpacePOSIX; + memset(fileName, 0, sizeof(fileName)); + + do + { + // TODO ASSUMPTION: File name is always resident + ASSERT(!pAttribName->GetHeader()->bNonResident); + + + // Get out all the info we need + NTFS_AttrFileName* pFileName = (NTFS_AttrFileName*)pAttribName->GetResidentData(); + + // There can be multiple filenames with different namespaces + // so choose the best one + if(NTFS_IsBetterNameSpace(nameSpace, pFileName->nameSpace)) + { + // Dates + NTFS_MakeFileTime(pFileName->timeCreated, ftCreated); + NTFS_MakeFileTime(pFileName->timeModified, ftModified); + NTFS_MakeFileTime(pFileName->timeRead, ftAccessed); + + // File Name + wcsncpy(fileName, (wchar_t*)(((byte*)pFileName) + sizeof(NTFS_AttrFileName)), pFileName->cFileName); + fileName[pFileName->cFileName] = 0; + + // Attributes + if(pFileName->flags & kNTFS_FileReadOnly) + fileAttrib |= FILE_ATTRIBUTE_READONLY; + if(pFileName->flags & kNTFS_FileHidden) + fileAttrib |= FILE_ATTRIBUTE_HIDDEN; + if(pFileName->flags & kNTFS_FileArchive) + fileAttrib |= FILE_ATTRIBUTE_ARCHIVE; + if(pFileName->flags & kNTFS_FileSystem) + fileAttrib |= FILE_ATTRIBUTE_SYSTEM; + + // Parent Directory + mftParent = NTFS_RefToSector(*pInfo, pFileName->refParent); + + // Namespace + nameSpace = pFileName->nameSpace; + } + } + while(pAttribName->NextAttribute(kNTFS_FILENAME)); + + + // Check if we got a file name + if(fileName[0] == 0) + RET_ERROR(ERROR_NTFS_INVALID); + + + + // Check if it's the root + // If so then bumm out cuz we don't want to have anything to do with it + if(mftRecord == mftParent || // Root is it's own parent + !wcscmp(fileName, L".") || // Or is called '.' + !wcscmp(fileName, kNTFS_MFTName)) // Or it's the MFT + RET_ERROR(ERROR_SUCCESS); + + + + // Create Parent folders + if(!ProcessMFTRecord(pInfo, mftParent, hIn)) + PASS_FATAL(); + + + + // If it's a folder then create it + if(pRecord->flags & kNTFS_RecFlagDir) + { + // Try to change to dir + if(!SetCurrentDirectoryW(fileName)) + { + // Otherwise create dir + if(CreateDirectoryW(fileName, NULL)) + { + // And set attributes + SetFileAttributesW(fileName, fileAttrib); + SetCurrentDirectoryW(fileName); + } + } + + wprintf(L"\\%s", fileName); + } + + + // Otherwise write the file data + else + { + // Write to the File + hFile = CreateFileW(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); + + uint16 nRename = 0; + wchar_t fileName2[MAX_PATH + 1]; + wcscpy(fileName2, fileName); + + // For duplicate files we add .x to the file name where x is a number + while(hFile == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_FILE_EXISTS + && nRename < 0x1000000) + { + wcscpy(fileName, fileName2); + wcscat(fileName, L"."); + uint16 len = wcslen(fileName); + + // Make sure we don't have a buffer overflow + if(len > MAX_PATH - 5) + break; + + _itow(nRename, fileName + len, 10); + nRename++; + + hFile = CreateFileW(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); + } + + wprintf(L"\\%s", fileName); + bFile = true; + + // Check if successful after all that + if(hFile == INVALID_HANDLE_VALUE) + PASS_FATAL(); + + + DWORD dwDone = 0; + uint64 fileSize = 0; + + + // Get the File's data + pAttribData = record.FindAttribute(kNTFS_DATA, hIn); + if(!pAttribData) RET_ERROR(ERROR_NTFS_INVALID); + + + // For Resident data just write it out + if(!pAttribData->GetHeader()->bNonResident) + { + if(!WriteFile(hFile, pAttribData->GetResidentData(), pAttribData->GetResidentSize(), &dwDone, NULL)) + PASS_FATAL(); + } + + // For Nonresident data a bit more involved + else + { + pDataRun = pAttribData->GetDataRun(); + ASSERT(pDataRun != NULL); + + NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)pAttribData->GetHeader(); + fileSize = pNonRes->cbAttribData; + + // Allocate a cluster for reading and writing + NTFS_Cluster clus; + if(!clus.New(pInfo)) + RET_FATAL(ERROR_NOT_ENOUGH_MEMORY); + + + // Now loop through the data run + if(pDataRun->First()) + { + do + { + // If it's a sparse cluster then just write zeros + if(pDataRun->m_bSparse) + { + memset(clus.m_pCluster, 0, clus.m_cbCluster); + + for(uint32 i = 0; i < pDataRun->m_numClusters && fileSize; i++) + { + DWORD dwToWrite = clus.m_cbCluster; + if(!HIGHDWORD(fileSize) && dwToWrite > (DWORD)fileSize) + dwToWrite = (DWORD)fileSize; + + if(!WriteFile(hFile, clus.m_pCluster, dwToWrite, &dwDone, NULL)) + PASS_FATAL(); + + fileSize -= dwToWrite; + } + } + + // Not sparse + else + { + // Add a lock on those clusters so we don't have to scrounge'm later + AddLocationLock(pInfo, CLUSTER_TO_SECTOR(*pInfo, pDataRun->m_firstCluster), + CLUSTER_TO_SECTOR(*pInfo, pDataRun->m_firstCluster + pDataRun->m_numClusters)); + + // Read and write clusters out + for(uint32 i = 0; i < pDataRun->m_numClusters && fileSize; i++) + { + DWORD dwToWrite = min(clus.m_cbCluster, (DWORD)fileSize); + uint64 sector = CLUSTER_TO_SECTOR(*pInfo, (pDataRun->m_firstCluster + i)); + + if(!clus.Read(pInfo, sector, hIn)) + PASS_ERROR(); + + if(!WriteFile(hFile, clus.m_pCluster, dwToWrite, &dwDone, NULL)) + PASS_FATAL(); + + fileSize -= dwToWrite; + } + } + } + while(pDataRun->Next()); + } + } + + // TODO: More intelligence needed here + if(fileSize != 0) + printf(" (Entire file not written)"); + + SetFileTime(hFile, &ftCreated, &ftAccessed, &ftModified); + + CloseHandle(hFile); + hFile = NULL; + + SetFileAttributesW(fileName, fileAttrib); + } + } + + bRet = TRUE; + ::SetLastError(ERROR_SUCCESS); + +clean_up: + if(hFile && hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + if(pAttribName) + delete pAttribName; + if(pAttribData) + delete pAttribData; + if(pDataRun) + delete pDataRun; + + if(bFile) + { + if(::GetLastError() != ERROR_SUCCESS) + { + printf(" ("); + PrintLastError(); + fputc(')', stdout); + } + } + + + return bRet; +} + + +// ---------------------------------------------------------------------- +// Helper function to print out errors + +void PrintLastError() +{ + DWORD dwErr = ::GetLastError(); + switch(dwErr) + { + case ERROR_NTFS_INVALID: + printf("Invalid NTFS data structure"); + break; + + case ERROR_NTFS_NOTIMPLEMENT: + printf("NTFS feature not implemented"); + break; + + default: + { + LPVOID lpMsgBuf; + + DWORD dwRet = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, + dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL); + + if(dwRet && lpMsgBuf) + { + // Remove return + ((LPTSTR)lpMsgBuf)[dwRet - 2] = 0; + + wprintf((LPTSTR)lpMsgBuf); + + // Free the buffer. + ::LocalFree(lpMsgBuf); + } + } + }; +} + + +// ---------------------------------------------------------------------- +// Scrounge the partition for MFT Records and hand off to +// ProcessMFTRecord for processing + +BOOL ScroungeMFTRecords(PartitionInfo* pInfo, HANDLE hIn) +{ + byte buffSec[kSectorSize]; + DWORD dwDummy = 0; + + uint64 numRecords = 0; + + // Save current directory away + TCHAR curDir[MAX_PATH + 1]; + GetCurrentDirectory(MAX_PATH, curDir); + + // Loop through sectors + for(uint64 sec = pInfo->firstSector; sec < pInfo->lastSector; sec++) + { + // See if the current sector has already been read + if(CheckLocationLock(pInfo, sec)) + { + // TODO: check this + sec--; + continue; + } + + // Read the mftRecord + uint64 offRecord = SECTOR_TO_BYTES(sec); + LONG lHigh = HIGHDWORD(offRecord); + if(SetFilePointer(hIn, LOWDWORD(offRecord), &lHigh, FILE_BEGIN) == -1 + && GetLastError() != NO_ERROR) + return FALSE; + + if(!ReadFile(hIn, buffSec, kSectorSize, &dwDummy, NULL)) + return FALSE; + + // Check beginning of sector for the magic signature + if(!memcmp(&kNTFS_RecMagic, &buffSec, sizeof(kNTFS_RecMagic))) + { + // Move to right output directory + SetCurrentDirectory(curDir); + + // Then process it + BOOL bRet = ProcessMFTRecord(pInfo, sec, hIn); + + fputc('\n', stdout); + + if(!bRet) + return FALSE; + + } + } + + return TRUE; +} + + +// ---------------------------------------------------------------------- +// Output strings + +const char kPrintHeader[] = "Scrounge (NTFS) Version 0.7\n\n"; + +const char kPrintData[] = "\ + Start Sector End Sector Cluster Size MFT Offset \n\ +==================================================================\n\ +"; + +const char kPrintDrive[] = "\nDrive: %u\n"; +const char kPrintDriveInfo[] = " %-15u %-15u "; +const char kPrintNTFSInfo[] = "%-15u %-15u"; + +const char kPrintHelp[] = "\ +Recovers an NTFS partition with a corrupted MFT. \n\ + \n\ +Usage: scrounge drive start end cluster mft [outdir] \n\ + \n\ + drive: Physical drive number. \n\ + start: First sector of partition. \n\ + end: Last sector of partition. \n\ + cluster: Cluster size for the partition (in sectors). \n\ + mft: Offset from beginning of partition to MFT (in sectors). \n\ + outdir: Output directory (optional). \n\ + \n\ +"; + + +// ---------------------------------------------------------------------- +// Info functions + +int PrintNTFSInfo(HANDLE hDrive, uint64 tblSector) +{ + byte sector[kSectorSize]; + + uint64 pos = SECTOR_TO_BYTES(tblSector); + LONG lHigh = HIGHDWORD(pos); + if(SetFilePointer(hDrive, LOWDWORD(pos), &lHigh, FILE_BEGIN) == -1 + && GetLastError() != NO_ERROR) + return 1; + + DWORD dwRead = 0; + if(!ReadFile(hDrive, sector, kSectorSize, &dwRead, NULL)) + return 1; + + NTFS_BootSector* pBoot = (NTFS_BootSector*)sector; + if(!memcmp(pBoot->sysId, kNTFS_SysId, sizeof(pBoot->sysId))) + printf(kPrintNTFSInfo, pBoot->secPerClus, pBoot->offMFT * pBoot->secPerClus); + + wprintf(L"\n"); + return 0; +} + + +int PrintPartitionInfo(HANDLE hDrive, uint64 tblSector) +{ + ASSERT(sizeof(Drive_MBR) == kSectorSize); + Drive_MBR mbr; + + uint64 pos = SECTOR_TO_BYTES(tblSector); + LONG lHigh = HIGHDWORD(pos); + if(SetFilePointer(hDrive, LOWDWORD(pos), &lHigh, FILE_BEGIN) == -1 + && GetLastError() != NO_ERROR) + return 1; + + DWORD dwRead = 0; + if(!ReadFile(hDrive, &mbr, sizeof(Drive_MBR), &dwRead, NULL)) + return 1; + + if(mbr.sig == kMBR_Sig) + { + for(int i = 0; i < 4; i++) + { + if(mbr.partitions[i].system == kPartition_Extended || + mbr.partitions[i].system == kPartition_ExtendedLBA) + { + PrintPartitionInfo(hDrive, tblSector + mbr.partitions[i].startsec); + } + else if(!mbr.partitions[i].system == kPartition_Invalid) + { + printf(kPrintDriveInfo, (uint32)tblSector + mbr.partitions[i].startsec, (uint32)tblSector + mbr.partitions[i].endsec); + PrintNTFSInfo(hDrive, tblSector + (uint64)mbr.partitions[i].startsec); + } + } + } + + return 0; +} + +const WCHAR kDriveName[] = L"\\\\.\\PhysicalDrive%d"; + +int PrintData() +{ + printf(kPrintHeader); + printf(kPrintData); + + WCHAR driveName[MAX_PATH]; + + // LIMIT: 256 Drives + for(int i = 0; i < 0x100; i++) + { + wsprintf(driveName, kDriveName, i); + + HANDLE hDrive = CreateFile(driveName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hDrive != INVALID_HANDLE_VALUE) + { + printf(kPrintDrive, i); + + PrintPartitionInfo(hDrive, 0); + + CloseHandle(hDrive); + } + } + + return 2; +} + + +// ---------------------------------------------------------------------- +// Main Program + +int main(int argc, char* argv[]) +{ + int curArg = 1; + + if(argc < 2) + return PrintData(); + + + // Check for flags + if(*(argv[curArg]) == '-' || *(argv[curArg]) == '/' ) + { + char* arg = argv[curArg]; + arg++; + + while(*arg != '\0') + { + switch(tolower(*arg)) + { + + // Help + case 'h': + printf(kPrintHeader); + printf(kPrintHelp); + return 2; + + default: + printf("scrounge: invalid option '%c'\n", *arg); + return PrintData(); + } + + arg++; + } + + curArg++; + } + + PartitionInfo* pInfo = CreatePartitionInfo(); + if(!pInfo) + { + printf("scrounge: Out of Memory.\n"); + return 1; + } + + if(curArg + 5 > argc) + { + printf("scrounge: invalid option(s).\n"); + return 2; + } + + // Next param should be the drive + byte driveNum = atoi(argv[curArg++]); + + // Followed by the partition info + pInfo->firstSector = atoi(argv[curArg++]); + pInfo->lastSector = atoi(argv[curArg++]); + pInfo->clusterSize = atoi(argv[curArg++]); + pInfo->offMFT = atoi(argv[curArg++]); + + if(pInfo->firstSector == 0 || + pInfo->lastSector == 0 || + pInfo->clusterSize == 0 || + pInfo->offMFT == 0) + { + printf("scrounge: invalid option(s).\n"); + return 2; + } + +// pInfo->clusterSize = 8; +// pInfo->firstSector = 20482938/*128*/; +// pInfo->lastSector = 80019765/*15358077*/; +// pInfo->offMFT = 32; + + if(curArg <= argc) + SetCurrentDirectoryA(argv[curArg++]); + + WCHAR driveName[MAX_PATH]; + + wsprintf(driveName, kDriveName, driveNum); + + HANDLE hDrive = CreateFile(driveName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hDrive == INVALID_HANDLE_VALUE) + { + printf("scrounge: Can't open drive %d.\n", driveNum); + return 2; + } + + if(!ScroungeMFTRecords(pInfo, hDrive)) + { + printf("scrounge: "); + PrintLastError(); + fputc('\n', stdout); + return 2; + } + + FreePartitionInfo(pInfo); + + return 0; +} \ No newline at end of file diff --git a/src/scrounge.h b/src/scrounge.h new file mode 100644 index 0000000..dd908ea --- /dev/null +++ b/src/scrounge.h @@ -0,0 +1,33 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + +#ifndef __SCROUNGE_H__ +#define __SCROUNGE_H__ + + +BOOL ProcessMFTRecord(PartitionInfo* pInfo, uint64 mftRecord, HANDLE hIn); +BOOL ScroungeMFTRecords(PartitionInfo* pInfo, HANDLE hIn); +void PrintLastError(); + + + +#endif //__SCROUNGE_H__ \ No newline at end of file diff --git a/src/usuals.h b/src/usuals.h new file mode 100644 index 0000000..4193675 --- /dev/null +++ b/src/usuals.h @@ -0,0 +1,45 @@ +// +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.7 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +// + +#ifndef __USUALS_H__20010822 +#define __USUALS_H__20010822 + +#include + +typedef unsigned __int64 uint64; +typedef unsigned long uint32; +typedef unsigned short uint16; +typedef unsigned char byte; + +typedef signed __int64 int64; +typedef signed long int32; +typedef signed short int16; + +#ifndef NULL +#define NULL 0 +#endif + + +#define HIGHDWORD(i64) (DWORD)((i64) >> 32) +#define LOWDWORD(i64) (DWORD)((i64) & 0xFFFFFFFF) + +#endif //__USUALS_H__20010822 diff --git a/win32/win32.dsp b/win32/win32.dsp new file mode 100644 index 0000000..dbb5213 --- /dev/null +++ b/win32/win32.dsp @@ -0,0 +1,147 @@ +# Microsoft Developer Studio Project File - Name="Scrounge" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Scrounge - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Scrounge.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Scrounge.mak" CFG="Scrounge - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Scrounge - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Scrounge - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Scrounge - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "UNICODE" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /opt:nowin98 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "Scrounge - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "UNICODE" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "Scrounge - Win32 Release" +# Name "Scrounge - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\drive.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\memref.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\ntfs.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\ntfsx.cpp +# End Source File +# Begin Source File + +SOURCE=.\Scrounge.cpp +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\drive.h +# End Source File +# Begin Source File + +SOURCE=.\ntfs.h +# End Source File +# Begin Source File + +SOURCE=.\ntfsx.h +# End Source File +# Begin Source File + +SOURCE=.\scrounge.h +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win32/win32.dsw b/win32/win32.dsw new file mode 100644 index 0000000..b27fd20 --- /dev/null +++ b/win32/win32.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Scrounge"=.\Scrounge.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + -- cgit v1.2.3