A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about user-mode development.
 #7950  by Fyyre
 Fri Aug 12, 2011 12:07 am
dtox wrote:Hello,

How i can get the sector offset of a file (ex: c:\file.exe) please ?
Because i try to read a file directly from the disk whith CreateFile("\\.C\".., ReadFile(..sector_offset..

Perhaps, there are a method more elegant for read file directly (ring3) ?
Thx !

try this... ms-rem code, from years ago. still works great today (x64 as well).
Attachments
(3.83 KiB) Downloaded 96 times
 #7986  by Brock
 Sat Aug 13, 2011 7:35 pm
MS-REM code example works nicely, nice share Fyyre.
 #11030  by Brock
 Sat Jan 14, 2012 12:12 pm
I have ported this code to Delphi/Pascal with some much needed enhancements - much appreciated so returned respect was in order. Thanks Fyyre for sharing
Code: Select all
unit uRawCopyFile;

{***************************************************************}
{       (uRawCopyFile.pas)                                      }
{                                                               }
{        Raw Copy File From Disk                                }
{        Win2K / WinXP / Win2K3 / Win2k8 / Vista / Win7 -       }
{                                                               }
{        ---> Full Credit to MS-REM <---                        }
{                                                               }
{        +++ Added Unicode function                             }
{        +++ Added Copying files with progress/percentage       }
{        +++ Virtual memory handled by OS not RTL memory mgr    }
{                                                               }
{        By: Brock Williams - Torseq Technologies               }
{                                                               }
{***************************************************************}

interface


uses
  Windows;


type
   LPCOMPLETION_ROUTINE = ^COMPLETION_ROUTINE;
   COMPLETION_ROUTINE = Procedure(dwProgress: ULONG); stdcall;


function CopyFileA(lpSrcName: LPCSTR;
                   lpDstName: LPCSTR;
                   ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;

function CopyFileW(lpSrcName: LPCWSTR;
                   lpDstName: LPCWSTR;
                   ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;


implementation

{$O+}             // optimization enabled
{$Q-}             // disable overflow checking
{$R-}             // disable range checking
{$D-}             // no debug info
{$L-}             // no local symbol info
{$T-}             // type-checked pointers off - @ references are generic

const
  FILE_READ_ATTRIBUTES           = $00000080;
  FILE_DEVICE_FILE_SYSTEM        = $00000009;
  FILE_ANY_ACCESS                = $00000000;
  METHOD_NEITHER                 = $00000003;
  FSCTL_GET_RETRIEVAL_POINTERS   = ((FILE_DEVICE_FILE_SYSTEM shl 16) or
                                   (FILE_ANY_ACCESS shl 14) or
                                   (28 shl 2) or METHOD_NEITHER);

                                   
type
  RPBExtends = record
    NextVcn: LARGE_INTEGER;
        Lcn: LARGE_INTEGER;
  end;


type
  STARTING_VCN_INPUT_BUFFER = record
    StartingVcn: LARGE_INTEGER;
  end;


type
  RETRIEVAL_POINTERS_BUFFER = record
    ExtentCount: ULONG;
    StartingVcn: LARGE_INTEGER;
        Extends: Array [0..0] of RPBExtends;
  end;


type
  PCLUSTER = ^Cluster;
    CLUSTER = Array [0..0] of LONGLONG;



function GetFileClusters(lpFileName: LPCWSTR; ClusterSize: ULONG;
                         ClCount: PULONG; FileSize: PULONG): PCLUSTER;
var
  hFile: ULONG;
  OutSize: ULONG;
  Bytes, Cls, CnCount, r: ULONG;
  PrevVCN, Lcn: LARGE_INTEGER;
  InBuf: STARTING_VCN_INPUT_BUFFER;
  OutBuf: ^RETRIEVAL_POINTERS_BUFFER;
begin
     Result := nil;
     hFile := CreateFileW(lpFileName, FILE_READ_ATTRIBUTES,
    (FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE), nil, OPEN_EXISTING, 0, 0);
    if (hFile <> INVALID_HANDLE_VALUE) then
    begin
      FileSize^ := GetFileSize(hFile, nil);
      OutSize := sizeof(RETRIEVAL_POINTERS_BUFFER) + (FileSize^ div ClusterSize) * sizeof(OutBuf^.Extends);
      OutBuf := VirtualAlloc(nil, OutSize, MEM_COMMIT, PAGE_READWRITE);
      InBuf.StartingVcn.QuadPart := 0;
    if DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, @InBuf,
                            sizeof(InBuf), OutBuf, OutSize, Bytes, nil) then
        begin
          ClCount^ := ((FileSize^ + (ClusterSize - 1)) div ClusterSize);
          Result := VirtualAlloc(nil, (ClCount^ * sizeof(Result^)), MEM_COMMIT, PAGE_READWRITE);
          PrevVCN := OutBuf^.StartingVcn;
          Cls := 0;
        if (OutBuf^.ExtentCount > 0) then
        for r := 0 to OutBuf^.ExtentCount -1 do
           begin
               Lcn := OutBuf^.Extends[r].Lcn;
               CnCount := (OutBuf^.Extends[r].NextVcn.QuadPart - PrevVCN.QuadPart);
               while (cnCount > 0) do
               begin
                Result^[Cls] := Lcn.QuadPart;
                Inc(Lcn.QuadPart);
                Inc(Cls);
                Dec(CnCount);
               end;
                PrevVCN := OutBuf^.Extends[r].NextVcn;
            end;
        end;
        VirtualFree(OutBuf, 0, MEM_RELEASE);
        CloseHandle(hFile);
    end;
end;



function _CopyFile(lpSrcName: LPCWSTR; lpDstName: LPCWSTR;
                   PfnCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
var
    ClusterSize, BlockSize: ULONG;
    Clusters: PCLUSTER;
    ClCount, FileSize, Bytes: ULONG;
    hDrive, hFile: ULONG;
    SecPerCl, BtPerSec, r: ULONG;
    Buf: Pointer;
    Offset: LARGE_INTEGER;
    cDrv: Array [0..6] of WCHAR;
    BytesWritten, iFileSize: ULONG;
    PrevProgress, Progress: ULONG;
begin
    cDrv[0] := lpSrcName[0];
    cDrv[1] := ':';
    cDrv[2] := #0;
    GetDiskFreeSpaceW(cDrv, SecPerCl, BtPerSec, ULONG(nil^), ULONG(nil^));
    ClusterSize := (SecPerCl * BtPerSec);
    Clusters := GetFileClusters(lpSrcName, ClusterSize, @ClCount, @FileSize);
    iFileSize := FileSize;
    BytesWritten := 0;
    if (Clusters <> nil) then
    begin
        cDrv[0] := '\';
        cDrv[1] := '\';
        cDrv[2] := '.';
        cDrv[3] := '\';
        cDrv[4] := lpSrcName[0];
        cDrv[5] := ':';
        cDrv[6] := #0;
        hDrive := CreateFileW(cDrv, GENERIC_READ, (FILE_SHARE_READ or FILE_SHARE_WRITE),
        nil, OPEN_EXISTING, 0, 0);
        if (hDrive <> INVALID_HANDLE_VALUE) then
        begin
            hFile := CreateFileW(lpDstName, GENERIC_WRITE, 0, nil, CREATE_NEW, 0, 0);
            if (hFile <> INVALID_HANDLE_VALUE) then
            begin
                Buf := VirtualAlloc(nil, ClusterSize, MEM_COMMIT, PAGE_READWRITE);
                PrevProgress := 0;
                if (ClCount > 0) then
                for r := 0 to ClCount -1 do
                begin
                    Offset.QuadPart := (ClusterSize * Clusters^[r]);
                    SetFilePointer(hDrive, Offset.LowPart, @Offset.HighPart, FILE_BEGIN);
                    ReadFile(hDrive, Buf^, ClusterSize, Bytes, nil);
                    if (FileSize < ClusterSize) then
                    BlockSize := FileSize
                else
                    BlockSize := ClusterSize;
                    if WriteFile(hFile, Buf^, BlockSize, Bytes, nil) then
                    Inc(BytesWritten, Bytes);
                    Dec(FileSize, BlockSize);
                    Progress := Round((BytesWritten / iFileSize) * 100);
                    if (PrevProgress <> Progress) then
                    begin
                    Inc(PrevProgress);
                    if (PfnCallback <> nil) then
                    PfnCallback^(Progress);
                    end;
                end;
                VirtualFree(Buf, 0, MEM_RELEASE);
                CloseHandle(hFile);
            end;
            CloseHandle(hDrive);
        end;
        VirtualFree(Clusters, 0, MEM_RELEASE);
    end;
     result := (BytesWritten > 0) and (iFileSize = BytesWritten);
end;



function CopyFileA(lpSrcName: LPCSTR; lpDstName: LPCSTR;
                   ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
var lpCallback: LPCOMPLETION_ROUTINE;
begin
  if ProgressCallback = nil then
  lpCallback := nil
else
  lpCallback := @ProgressCallback;
  result := _CopyFile(LPCWSTR(WideString(lpSrcName)),
                      LPCWSTR(WideString(lpDstName)), lpCallback);
end;



function CopyFileW(lpSrcName: LPCWSTR; lpDstName: LPCWSTR;
                   ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
var lpCallback: LPCOMPLETION_ROUTINE;
begin
  if ProgressCallback = nil then
  lpCallback := nil
else
  lpCallback := @ProgressCallback;
  result := _CopyFile(lpSrcName, lpDstName, lpCallback);
end;


end.
 #11854  by Dmitry Varshavsky
 Mon Feb 27, 2012 8:34 pm
Brock wrote:I have ported this code to Delphi/Pascal with some much needed enhancements - much appreciated so returned respect was in order. Thanks Fyyre for sharing
Code: Select all
unit uRawCopyFile;

{***************************************************************}
{       (uRawCopyFile.pas)                                      }
{                                                               }
{        Raw Copy File From Disk                                }
{        Win2K / WinXP / Win2K3 / Win2k8 / Vista / Win7 -       }
{                                                               }
{        ---> Full Credit to MS-REM <---                        }
{                                                               }
{        +++ Added Unicode function                             }
{        +++ Added Copying files with progress/percentage       }
{        +++ Virtual memory handled by OS not RTL memory mgr    }
{                                                               }
{        By: Brock Williams - Torseq Technologies               }
{                                                               }
{***************************************************************}

interface


uses
  Windows;


type
   LPCOMPLETION_ROUTINE = ^COMPLETION_ROUTINE;
   COMPLETION_ROUTINE = Procedure(dwProgress: ULONG); stdcall;


function CopyFileA(lpSrcName: LPCSTR;
                   lpDstName: LPCSTR;
                   ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;

function CopyFileW(lpSrcName: LPCWSTR;
                   lpDstName: LPCWSTR;
                   ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;


implementation

{$O+}             // optimization enabled
{$Q-}             // disable overflow checking
{$R-}             // disable range checking
{$D-}             // no debug info
{$L-}             // no local symbol info
{$T-}             // type-checked pointers off - @ references are generic

const
  FILE_READ_ATTRIBUTES           = $00000080;
  FILE_DEVICE_FILE_SYSTEM        = $00000009;
  FILE_ANY_ACCESS                = $00000000;
  METHOD_NEITHER                 = $00000003;
  FSCTL_GET_RETRIEVAL_POINTERS   = ((FILE_DEVICE_FILE_SYSTEM shl 16) or
                                   (FILE_ANY_ACCESS shl 14) or
                                   (28 shl 2) or METHOD_NEITHER);

                                   
type
  RPBExtends = record
    NextVcn: LARGE_INTEGER;
        Lcn: LARGE_INTEGER;
  end;


type
  STARTING_VCN_INPUT_BUFFER = record
    StartingVcn: LARGE_INTEGER;
  end;


type
  RETRIEVAL_POINTERS_BUFFER = record
    ExtentCount: ULONG;
    StartingVcn: LARGE_INTEGER;
        Extends: Array [0..0] of RPBExtends;
  end;


type
  PCLUSTER = ^Cluster;
    CLUSTER = Array [0..0] of LONGLONG;



function GetFileClusters(lpFileName: LPCWSTR; ClusterSize: ULONG;
                         ClCount: PULONG; FileSize: PULONG): PCLUSTER;
var
  hFile: ULONG;
  OutSize: ULONG;
  Bytes, Cls, CnCount, r: ULONG;
  PrevVCN, Lcn: LARGE_INTEGER;
  InBuf: STARTING_VCN_INPUT_BUFFER;
  OutBuf: ^RETRIEVAL_POINTERS_BUFFER;
begin
     Result := nil;
     hFile := CreateFileW(lpFileName, FILE_READ_ATTRIBUTES,
    (FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE), nil, OPEN_EXISTING, 0, 0);
    if (hFile <> INVALID_HANDLE_VALUE) then
    begin
      FileSize^ := GetFileSize(hFile, nil);
      OutSize := sizeof(RETRIEVAL_POINTERS_BUFFER) + (FileSize^ div ClusterSize) * sizeof(OutBuf^.Extends);
      OutBuf := VirtualAlloc(nil, OutSize, MEM_COMMIT, PAGE_READWRITE);
      InBuf.StartingVcn.QuadPart := 0;
    if DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, @InBuf,
                            sizeof(InBuf), OutBuf, OutSize, Bytes, nil) then
        begin
          ClCount^ := ((FileSize^ + (ClusterSize - 1)) div ClusterSize);
          Result := VirtualAlloc(nil, (ClCount^ * sizeof(Result^)), MEM_COMMIT, PAGE_READWRITE);
          PrevVCN := OutBuf^.StartingVcn;
          Cls := 0;
        if (OutBuf^.ExtentCount > 0) then
        for r := 0 to OutBuf^.ExtentCount -1 do
           begin
               Lcn := OutBuf^.Extends[r].Lcn;
               CnCount := (OutBuf^.Extends[r].NextVcn.QuadPart - PrevVCN.QuadPart);
               while (cnCount > 0) do
               begin
                Result^[Cls] := Lcn.QuadPart;
                Inc(Lcn.QuadPart);
                Inc(Cls);
                Dec(CnCount);
               end;
                PrevVCN := OutBuf^.Extends[r].NextVcn;
            end;
        end;
        VirtualFree(OutBuf, 0, MEM_RELEASE);
        CloseHandle(hFile);
    end;
end;



function _CopyFile(lpSrcName: LPCWSTR; lpDstName: LPCWSTR;
                   PfnCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
var
    ClusterSize, BlockSize: ULONG;
    Clusters: PCLUSTER;
    ClCount, FileSize, Bytes: ULONG;
    hDrive, hFile: ULONG;
    SecPerCl, BtPerSec, r: ULONG;
    Buf: Pointer;
    Offset: LARGE_INTEGER;
    cDrv: Array [0..6] of WCHAR;
    BytesWritten, iFileSize: ULONG;
    PrevProgress, Progress: ULONG;
begin
    cDrv[0] := lpSrcName[0];
    cDrv[1] := ':';
    cDrv[2] := #0;
    GetDiskFreeSpaceW(cDrv, SecPerCl, BtPerSec, ULONG(nil^), ULONG(nil^));
    ClusterSize := (SecPerCl * BtPerSec);
    Clusters := GetFileClusters(lpSrcName, ClusterSize, @ClCount, @FileSize);
    iFileSize := FileSize;
    BytesWritten := 0;
    if (Clusters <> nil) then
    begin
        cDrv[0] := '\';
        cDrv[1] := '\';
        cDrv[2] := '.';
        cDrv[3] := '\';
        cDrv[4] := lpSrcName[0];
        cDrv[5] := ':';
        cDrv[6] := #0;
        hDrive := CreateFileW(cDrv, GENERIC_READ, (FILE_SHARE_READ or FILE_SHARE_WRITE),
        nil, OPEN_EXISTING, 0, 0);
        if (hDrive <> INVALID_HANDLE_VALUE) then
        begin
            hFile := CreateFileW(lpDstName, GENERIC_WRITE, 0, nil, CREATE_NEW, 0, 0);
            if (hFile <> INVALID_HANDLE_VALUE) then
            begin
                Buf := VirtualAlloc(nil, ClusterSize, MEM_COMMIT, PAGE_READWRITE);
                PrevProgress := 0;
                if (ClCount > 0) then
                for r := 0 to ClCount -1 do
                begin
                    Offset.QuadPart := (ClusterSize * Clusters^[r]);
                    SetFilePointer(hDrive, Offset.LowPart, @Offset.HighPart, FILE_BEGIN);
                    ReadFile(hDrive, Buf^, ClusterSize, Bytes, nil);
                    if (FileSize < ClusterSize) then
                    BlockSize := FileSize
                else
                    BlockSize := ClusterSize;
                    if WriteFile(hFile, Buf^, BlockSize, Bytes, nil) then
                    Inc(BytesWritten, Bytes);
                    Dec(FileSize, BlockSize);
                    Progress := Round((BytesWritten / iFileSize) * 100);
                    if (PrevProgress <> Progress) then
                    begin
                    Inc(PrevProgress);
                    if (PfnCallback <> nil) then
                    PfnCallback^(Progress);
                    end;
                end;
                VirtualFree(Buf, 0, MEM_RELEASE);
                CloseHandle(hFile);
            end;
            CloseHandle(hDrive);
        end;
        VirtualFree(Clusters, 0, MEM_RELEASE);
    end;
     result := (BytesWritten > 0) and (iFileSize = BytesWritten);
end;



function CopyFileA(lpSrcName: LPCSTR; lpDstName: LPCSTR;
                   ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
var lpCallback: LPCOMPLETION_ROUTINE;
begin
  if ProgressCallback = nil then
  lpCallback := nil
else
  lpCallback := @ProgressCallback;
  result := _CopyFile(LPCWSTR(WideString(lpSrcName)),
                      LPCWSTR(WideString(lpDstName)), lpCallback);
end;



function CopyFileW(lpSrcName: LPCWSTR; lpDstName: LPCWSTR;
                   ProgressCallback: LPCOMPLETION_ROUTINE = nil): BOOL; stdcall;
var lpCallback: LPCOMPLETION_ROUTINE;
begin
  if ProgressCallback = nil then
  lpCallback := nil
else
  lpCallback := @ProgressCallback;
  result := _CopyFile(lpSrcName, lpDstName, lpCallback);
end;


end.
This "old" and proven code has a bug:
ClusterCount is calculated correctly - ( FileSize + ClusterSize - 1 ) / ClusterSize, but extent list returned from FSCTL_GET_RETRIEVAL_POINTERS request could consist of a larger number of clusters and you will get the buffer overflow with corresponding heap corruption. You have to break the cycle when "Cls" variable reaches initially calculated cluster count. Pls fix it if you want to use this code in commercial product..
 #11973  by Tigzy
 Mon Mar 05, 2012 3:27 pm
Hello

This code works well with ZeroAccess, I'm able to find forged sys files. But on other infections I'm bypassed...

I guess this is due to that part which is easily bypassable:
Code: Select all
ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
ObjectAttributes.ObjectName = NativePath;
ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE;
      
NtStatus = NtCreateFile(
                        &FileHandle,       // FileHandle
                        GENERIC_READ |
                        GENERIC_WRITE,     // DesiredAccess
                        &ObjectAttributes, // ObjectAttributes
                        &IoStatusBlock,    // IoStatusBlock
                        NULL,              // AllocationSize OPTIONAL
                        0,                 // FileAttributes
                        FILE_SHARE_READ |
                        FILE_SHARE_WRITE,  // ShareAccess
                        FILE_OPEN,         // CreateDisposition
                        0,                 // CreateOptions
                        NULL,              // EaBuffer OPTIONAL
                        0);                // EaLength
Does someone have a tip to go deeper, even with a kernelmode code?
I've heard about some atapi handle somewhere in the kernel wich can be useful...