Skip to content

Instantly share code, notes, and snippets.

@danzek
Created April 13, 2017 21:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danzek/d353b2f7af19a3886daf77319e34dc51 to your computer and use it in GitHub Desktop.
Save danzek/d353b2f7af19a3886daf77319e34dc51 to your computer and use it in GitHub Desktop.
Direct Copy
/* only works on NTFS: does not work for resident files (files within the $MFT)
* from http://www.rohitab.com/discuss/topic/24252-ntfs-directcopy-method-from-napalm/
* retrieved on April 13, 2017
* posted by user Napalm (http://www.rohitab.com/discuss/user/3860-napalm/) 09 April 2007 - 03:13 AM
DirectCopy v2.0 - by Napalm @ NetCore2K
------------------------------------
Please try and read and understand this source code. You will learn something.
Sector = 512 Bytes of disk space
Cluster = A Group of Sectors. This is different depending on your file
system. But normally its 4Kb so thats 8 sectors.
VCN = Virtual Cluster Number. Simply the index of the cluster within its context.
LCN = Logical Cluster Number. The physical cluster index on containing media.
Extent = The extent of a Cluster index.
The DirectCopy function invokes a Device Control Code to get the cluster information about a file.
We then loop though each resulting extent and copy each cluster to a new file. If the file has no
clusters assigned to it the data is stored in the MFT. This means the file data itself is in the
MFT since it would be a waste to allocate an entire cluster of sectors for lets say just 5 bytes.
*/
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
typedef struct{
ULONG Type; /* Magic number 'FILE' */
USHORT UsaOffset; /* Offset to the update sequence */
USHORT UsaCount; /* Size in words of Update Sequence Number & Array (S) */
ULONGLONG Lsn; /* $LogFile Sequence Number (LSN) */
} NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER;
typedef struct{
NTFS_RECORD_HEADER RecHdr;
USHORT SequenceNumber; /* Sequence number */
USHORT LinkCount; /* Hard link count */
USHORT AttributeOffset; /* Offset to the first Attribute */
USHORT Flags; /* Flags */
ULONG BytesInUse; /* Real size of the FILE record */
ULONG BytesAllocated; /* Allocated size of the FILE record */
ULONGLONG BaseFileRecord; /* Reference to the base FILE record */
USHORT NextAttributeNumber; /* Next Attribute Id */
USHORT Pading; /* Align to 4 byte boundary (XP) */
ULONG MFTRecordNumber; /* Number of this MFT Record (XP) */
USHORT UpdateSeqNum;
} FILE_RECORD_HEADER, *PFILE_RECORD_HEADER;
typedef enum{
AttributeStandardInformation = 0x10,
AttributeAttributeList = 0x20,
AttributeFileName = 0x30,
AttributeObjectId = 0x40,
AttributeSecurityDescriptor = 0x50,
AttributeVolumeName = 0x60,
AttributeVolumeInformation = 0x70,
AttributeData = 0x80,
AttributeIndexRoot = 0x90,
AttributeIndexAllocation = 0xA0,
AttributeBitmap = 0xB0,
AttributeReparsePoint = 0xC0,
AttributeEAInformation = 0xD0,
AttributeEA = 0xE0,
AttributePropertySet = 0xF0,
AttributeLoggedUtilityStream = 0x100
} ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE;
typedef struct{
ATTRIBUTE_TYPE AttributeType;
ULONG Length;
BOOLEAN Nonresident;
UCHAR NameLength;
USHORT NameOffset;
USHORT Flags;
USHORT AttributeNumber;
} ATTRIBUTE, *PATTRIBUTE;
typedef struct{
ATTRIBUTE Attribute;
ULONG ValueLength;
USHORT ValueOffset;
UCHAR Flags;
} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE;
BOOL DirectCopy(LPSTR lpszSrc, LPSTR lpszDest)
{
BOOL bResult = FALSE;
HANDLE hSrc = CreateFile(lpszSrc, FILE_READ_ATTRIBUTES, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), NULL, OPEN_EXISTING, 0, 0);
if(hSrc != INVALID_HANDLE_VALUE){
CHAR szDrive[7]; wsprintf(szDrive, "%c:", *lpszSrc);
DWORD dwSectorPerCluster, dwBytesPerSector;
GetDiskFreeSpace(szDrive, &dwSectorPerCluster, &dwBytesPerSector, NULL, NULL);
DWORD dwClusterSize = (dwBytesPerSector * dwSectorPerCluster);
LARGE_INTEGER liFileSize; liFileSize.LowPart = GetFileSize(hSrc, (LPDWORD)&liFileSize.HighPart);
DWORD dwClusters = (liFileSize.QuadPart / dwClusterSize);
DWORD dwRead, dwWritten, dwPointsSize = sizeof(RETRIEVAL_POINTERS_BUFFER) + (dwClusters * (sizeof(LARGE_INTEGER) * 2));
PRETRIEVAL_POINTERS_BUFFER pPoints = (PRETRIEVAL_POINTERS_BUFFER) new BYTE[dwPointsSize];
STARTING_VCN_INPUT_BUFFER vcnStart = { 0 };
if(DeviceIoControl(hSrc, FSCTL_GET_RETRIEVAL_POINTERS, &vcnStart, sizeof(vcnStart), pPoints, dwPointsSize, &dwWritten, NULL)){
wsprintf(szDrive, "\\\\.\\%c:", *lpszSrc);
HANDLE hDrive = CreateFile(szDrive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if(hDrive != INVALID_HANDLE_VALUE){
HANDLE hDest = CreateFile(lpszDest, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0);
if(hDest != INVALID_HANDLE_VALUE){
SetFilePointer(hDest, liFileSize.LowPart, &liFileSize.HighPart, FILE_BEGIN);
SetEndOfFile(hDest);
LPBYTE lpCluster = new BYTE[dwClusterSize];
LARGE_INTEGER vcnPrev = pPoints->StartingVcn;
for(DWORD dwExtent = 0; dwExtent < pPoints->ExtentCount; dwExtent++){
DWORD dwLength = (DWORD)(pPoints->Extents[dwExtent].NextVcn.QuadPart - vcnPrev.QuadPart);
LARGE_INTEGER liSrcPos = { (pPoints->Extents[dwExtent].Lcn.QuadPart * dwClusterSize) };
LARGE_INTEGER liDstPos = { (vcnPrev.QuadPart * dwClusterSize) };
for(DWORD dwCluster = 0; dwCluster < dwLength; dwCluster++){
SetFilePointer(hDrive, liSrcPos.LowPart, &liSrcPos.HighPart, FILE_BEGIN);
ReadFile(hDrive, lpCluster, dwClusterSize, &dwRead, NULL);
SetFilePointer(hDest, liDstPos.LowPart, &liDstPos.HighPart, FILE_BEGIN);
WriteFile(hDest, lpCluster, dwRead, &dwWritten, NULL);
liSrcPos.QuadPart += dwClusterSize; liDstPos.QuadPart += dwClusterSize;
}
vcnPrev = pPoints->Extents[dwExtent].NextVcn;
}
delete lpCluster;
CloseHandle(hDest);
bResult = TRUE;
}
CloseHandle(hDrive);
}
}else{
// The file is too small to have a cluster assignment, so we need to get the MFT record.
if(GetLastError() == ERROR_HANDLE_EOF){
CHAR szDrive[7];
wsprintf(szDrive, "\\\\.\\%c:", *lpszSrc);
HANDLE hDrive = CreateFile(szDrive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if(hDrive != INVALID_HANDLE_VALUE){
NTFS_VOLUME_DATA_BUFFER ntfsVolData;
if(DeviceIoControl(hDrive, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &ntfsVolData, sizeof(ntfsVolData), &dwWritten, NULL)){
BY_HANDLE_FILE_INFORMATION bhFileInfo;
if(GetFileInformationByHandle(hSrc, &bhFileInfo)){
DWORD dwOuputSize = (sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER) + ntfsVolData.BytesPerFileRecordSegment);
NTFS_FILE_RECORD_INPUT_BUFFER mftRecordInput = { { bhFileInfo.nFileIndexLow, bhFileInfo.nFileIndexHigh } };
PNTFS_FILE_RECORD_OUTPUT_BUFFER pmftRecordOutput = (PNTFS_FILE_RECORD_OUTPUT_BUFFER) new BYTE[dwOuputSize];
if(DeviceIoControl(hDrive, FSCTL_GET_NTFS_FILE_RECORD, &mftRecordInput, sizeof(mftRecordInput), pmftRecordOutput, dwOuputSize, &dwWritten, NULL)){
PFILE_RECORD_HEADER pFileRecord = (PFILE_RECORD_HEADER)pmftRecordOutput->FileRecordBuffer;
PATTRIBUTE pAttribute = (PATTRIBUTE)((LPBYTE)pFileRecord + pFileRecord->AttributeOffset);
LPBYTE lpData = NULL; DWORD dwSize = 0;
while((LPBYTE)pAttribute < (LPBYTE)(pFileRecord + pFileRecord->BytesInUse)){
if(pAttribute->AttributeType == AttributeData){
PRESIDENT_ATTRIBUTE pResAttrib = (PRESIDENT_ATTRIBUTE)pAttribute;
lpData = ((LPBYTE)pResAttrib + pResAttrib->ValueOffset);
dwSize = pResAttrib->ValueLength;
break;
}
pAttribute = (PATTRIBUTE)((LPBYTE)pAttribute + pAttribute->Length);
}
if(lpData != NULL){
HANDLE hDest = CreateFile(lpszDest, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0);
if(hDest != INVALID_HANDLE_VALUE){
WriteFile(hDest, lpData, dwSize, &dwWritten, NULL);
CloseHandle(hDest);
bResult = TRUE;
}
}
}
delete pmftRecordOutput;
}
}
CloseHandle(hDrive);
}
}
}
delete pPoints;
CloseHandle(hSrc);
}
return bResult;
}
int main(int argc, char *argv[])
{
printf("DirectCopy v2.0 - by Napalm @ NetCore2K\n");
if(argc < 3) return printf("Usage: %s <src> <dest>\n\n", argv[0]);
printf("Copying %s to %s ... ", argv[1], argv[2]);
printf("%s\n\n", (DirectCopy(argv[1], argv[2])?"OK":"Failed!"));
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment