A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about user-mode development.
 #7925  by dtox
 Wed Aug 10, 2011 5:39 pm
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 !
 #7927  by Alex
 Wed Aug 10, 2011 8:32 pm
Not perfect, but works...
Code: Select all
NTSTATUS GetFileVolumeHandle(
	IN UNICODE_STRING *NativePath,
	OUT HANDLE *VolumeHandle)
{
	NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;

	WCHAR VolumeLetter[8];

	UNICODE_STRING VolumeName;
	OBJECT_ATTRIBUTES ObjectAttributes = {0};
    IO_STATUS_BLOCK IoStatusBlock;


	__try
	{
		if(NativePath == NULL)
			return STATUS_INVALID_PARAMETER_1;

		if(NativePath->Buffer == NULL ||
		   NativePath->Length == 0)
		{
			return STATUS_INVALID_PARAMETER_1;
		}

		if(VolumeHandle == NULL)
			return STATUS_INVALID_PARAMETER_2;

		if(NativePath->Buffer[0] != L'\\' ||
		   NativePath->Buffer[1] != L'?'  ||
		   NativePath->Buffer[2] != L'?'  ||
		   NativePath->Buffer[3] != L'\\' ||
		   NativePath->Buffer[5] != L':')
		{
			return STATUS_OBJECT_PATH_SYNTAX_BAD;
		}

		*VolumeHandle = NULL;


		RtlZeroMemory(
					  VolumeLetter,
					  sizeof(VolumeLetter));
		
		NtStatus = StringCchPrintfW(
									VolumeLetter,
									sizeof(VolumeLetter),
									L"\\??\\%c:",
									NativePath->Buffer[4]);

		if(SUCCEEDED(NtStatus))
		{
			RtlInitUnicodeString(
								 &VolumeName,
								 VolumeLetter);

			ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
			ObjectAttributes.ObjectName = &VolumeName;
			ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE;
		
			NtStatus = NtCreateFile(
									VolumeHandle,      // 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

			if(NtStatus)
			{
				DbgPrint((
						  ">> GetFileVolumeHandle - NtCreateFile - %.8X\n",
						  NtStatus));
			}
		}
	}
    __except(GetExceptionCode() ==
			 EXCEPTION_BREAKPOINT ?
			 EXCEPTION_CONTINUE_SEARCH :
			 EXCEPTION_EXECUTE_HANDLER)
    {
		DbgPrint((
				  ">> GetFileVolumeHandle - %.8X\n",
				  GetExceptionCode()));

		return GetExceptionCode();
    }

    return NtStatus;
}

NTSTATUS GetFilePhysicalOffset(
	IN UNICODE_STRING *NativePath,
	OUT LONGLONG *PhysicalOffset)
{
	NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;

	HANDLE FileHandle;
	HANDLE VolumeHandle;
	ULONG RetrievalPointersLength = PAGE_SIZE;

	OBJECT_ATTRIBUTES ObjectAttributes = {0};
    IO_STATUS_BLOCK IoStatusBlock;

	STARTING_VCN_INPUT_BUFFER StartingVcn = {0};
	RETRIEVAL_POINTERS_BUFFER *RetrievalPointers = NULL;
	NTFS_VOLUME_DATA_BUFFER VolumeData;
	VOLUME_LOGICAL_OFFSET VolumeLogicalOffset;
	VOLUME_PHYSICAL_OFFSETS VolumePhysicalOffsets;
	

	__try
	{
		if(NativePath == NULL)
			return STATUS_INVALID_PARAMETER_1;

		if(NativePath->Buffer == NULL ||
		   NativePath->Length == 0)
		{
			return STATUS_INVALID_PARAMETER_1;
		}

		if(PhysicalOffset == NULL)
			return STATUS_INVALID_PARAMETER_2;

		if(NativePath->Buffer[0] != L'\\' ||
		   NativePath->Buffer[1] != L'?'  ||
		   NativePath->Buffer[2] != L'?'  ||
		   NativePath->Buffer[3] != L'\\' ||
		   NativePath->Buffer[5] != L':')
		{
			return STATUS_OBJECT_PATH_SYNTAX_BAD;
		}

		*PhysicalOffset = 0;


		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

		if(NtStatus)
		{
			DbgPrint((
					  ">> GetFilePhysicalOffset - NtCreateFile - %.8X\n",
					  NtStatus));
		}

		if(NT_SUCCESS(NtStatus))
		{
			do
			{
				RetrievalPointers = RtlAllocateMemory(RetrievalPointersLength);

				if(RetrievalPointers)
				{
					NtStatus = NtFsControlFile(
											   FileHandle,                        // FileHandle
											   NULL,                              // Event
											   NULL,                              // ApcRoutine
											   NULL,                              // ApcContext
											   &IoStatusBlock,                    // IoStatusBlock
											   FSCTL_GET_RETRIEVAL_POINTERS,      // FsControlCode
											   &StartingVcn,                      // InputBuffer
											   sizeof(STARTING_VCN_INPUT_BUFFER), // InputBufferLength
											   RetrievalPointers,                 // OutputBuffer
											   RetrievalPointersLength);          // OutputBufferLength
						
					if(NtStatus == STATUS_INFO_LENGTH_MISMATCH)
					{
						RtlFreeMemory(RetrievalPointers);

						RetrievalPointersLength *= 2;
					}
					else if(NT_ERROR(NtStatus))
					{
						DbgPrint((
								  ">> GetFilePhysicalOffset - FSCTL_GET_RETRIEVAL_POINTERS - %.8X\n",
								  NtStatus));

						RtlFreeMemory(RetrievalPointers);

						NtClose(FileHandle);

						return NtStatus;
					}
				}
				else
				{
					NtClose(FileHandle);

					return STATUS_INSUFFICIENT_RESOURCES;
				}

			} while(NtStatus == STATUS_INFO_LENGTH_MISMATCH);

			NtStatus = GetFileVolumeHandle(
											 NativePath,     // NativePath
											 &VolumeHandle); // VolumeHandle

			if(NT_SUCCESS(NtStatus))
			{
				NtStatus = NtFsControlFile(
										   VolumeHandle,                     // FileHandle
										   NULL,                             // Event
										   NULL,                             // ApcRoutine
										   NULL,                             // ApcContext
										   &IoStatusBlock,                   // IoStatusBlock
										   FSCTL_GET_NTFS_VOLUME_DATA,       // FsControlCode
										   NULL,                             // InputBuffer
										   0,                                // InputBufferLength
										   &VolumeData,                      // OutputBuffer
										   sizeof(NTFS_VOLUME_DATA_BUFFER)); // OutputBufferLength

				if(NtStatus)
				{
					DbgPrint((
							  ">> GetFilePhysicalOffset - FSCTL_GET_NTFS_VOLUME_DATA - %.8X\n",
							  NtStatus));
				}

				if(NT_SUCCESS(NtStatus))
				{
					VolumeLogicalOffset.LogicalOffset = RetrievalPointers->Extents[0].Lcn.QuadPart *
														VolumeData.BytesPerCluster;

					NtStatus = NtDeviceIoControlFile(
													 VolumeHandle,                     // FileHandle
													 NULL,                             // Event
													 NULL,                             // ApcRoutine
													 NULL,                             // ApcContext
													 &IoStatusBlock,                   // IoStatusBlock
													 IOCTL_VOLUME_LOGICAL_TO_PHYSICAL, // IoControlCode
													 &VolumeLogicalOffset,             // InputBuffer
													 sizeof(VOLUME_LOGICAL_OFFSET),    // InputBufferLength
													 &VolumePhysicalOffsets,           // OutputBuffer
													 sizeof(VOLUME_PHYSICAL_OFFSETS)); // OutputBufferLength

					if(NtStatus)
					{
						DbgPrint((
								  ">> GetFilePhysicalOffset - IOCTL_VOLUME_LOGICAL_TO_PHYSICAL - %.8X\n",
								  NtStatus));
					}

					if(NT_SUCCESS(NtStatus))
					{
						*PhysicalOffset = (LONGLONG)(VolumePhysicalOffsets.PhysicalOffset[0].Offset /
													 VolumeData.BytesPerSector);
					}
				}

				NtClose(VolumeHandle);
			}

			NtClose(FileHandle);

			RtlFreeMemory(RetrievalPointers);
		}
    }
    __except(GetExceptionCode() ==
			 EXCEPTION_BREAKPOINT ?
			 EXCEPTION_CONTINUE_SEARCH :
			 EXCEPTION_EXECUTE_HANDLER)
    {
		if(FileHandle)
			NtClose(FileHandle);

		if(VolumeHandle)
			NtClose(VolumeHandle);

		if(RetrievalPointers)
			RtlFreeMemory(RetrievalPointers);

		DbgPrint((
				  ">> GetFilePhysicalOffset - %.8X\n",
				  GetExceptionCode()));

		return GetExceptionCode();
    }

    return NtStatus;
}
 #7932  by Tigzy
 Thu Aug 11, 2011 7:07 am
Hello
I'm also looking for something like that...

What is the shortest way to read on the disk ?
I mean without using all the stack of Windows API that can be hooked by some piece of rootkit.
The closer we are from the hardware, the less we could be returned some forged HASH...
 #7933  by EP_X0FF
 Thu Aug 11, 2011 7:21 am
Tigzy wrote:The closer we are from the hardware, the less we could be returned some forged HASH...
..and the closer we are to complete incompatibility and BSOD-generation.
 #7940  by Tigzy
 Thu Aug 11, 2011 3:45 pm
..and the closer we are to complete incompatibility and BSOD-generation.
Of course :)
The game is to avoid BSOD.

But some apps can do that, for example TDSSKiller.
It can get both forged and real HASH for comparison.
 #7941  by EP_X0FF
 Thu Aug 11, 2011 3:50 pm
The abstraction layers you want to bypass were build specially to remove incompatibilities and all other kind of problems and make software work on big variety of hardware without any kind of hardcoding.
 #7942  by Tigzy
 Thu Aug 11, 2011 3:54 pm
Yeah, I know all that stuff...
And I know that some Rootkit attach them to the Disk driver stack to filter the IRPs and return bad handle on certain files.
That's why I want to go as deep as I can to bypass that kind of filter. Maybe this is a bit unrealistic, but as I said some apps can. So why not?
 #7943  by EP_X0FF
 Thu Aug 11, 2011 4:04 pm
And the next update of rootkit filter will filter out your request. And again and again. This is not 2008 year, and even not 2009. Filtering moved to much more lower level. They don't think about "compatibility" - if 100 bots from 1000 will BSOD, well not a big lose. Going deeper and bypassing OS I/O will bring you a huge variety of bugs, slowdowns, incompatibilities with different chipsets etc. And basically your app there will be still sleeping BSOD-generator. Yep, of course for proof-of-concept, or private tool this can be done. But in enterprise software you can't follow this way without huge impact.
The only one 100% working and safe way - is to cure patient while he is under narcosis. Everything else are just crutches.
 #7944  by Tigzy
 Thu Aug 11, 2011 4:51 pm
Yep, of course for proof-of-concept, or private tool this can be done. But in enterprise software you can't follow this way without huge impact.
This is it! At my level of knowledge, I didn't even thought about production product . Only for learning purpose

Well, do you have (even a small) idea about how TDSSKiller doing his job?
It's able to detect the latest Max++