A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about user-mode development.
 #24881  by Tigzy
 Fri Jan 09, 2015 7:45 am
Hello

http://msdn.microsoft.com/en-us/library ... 85%29.aspx
I'm trying to read a few sectors with ATA pass through interface (APTI), that works when I read the MBR because we read sector 0, of size 1. So even with putting shit into the CurrentTaskFile that works because almost all the fields are 0.

Now I'm trying to read in a given place, let's say sector 4000 (decimal) and that doesn't work.
The definition of CurrentTaskFile isn't the same on several places.

On MSDN it's the following:
0 Features Register
1 Sector Count Register
2 Sector Number Register
3 Cylinder Low Register
4 Cylinder High Register
5 Device/Head Register
6 Command Register
7 Reserved
And on a source I found: http://www.smartmontools.org/browser/tr ... a.cpp#L229
It's using an ATA command of size 12 or 16 (while ATA_PASS_THROUGH_DIRECT structure has CurrentTaskFile of size 8), and they put LBA inside while MSDN tells to use CHS.

What is the correct way to use it?
My code:
Code: Select all
void Disk::FillAPTI(PATA_PASS_THROUGH_DIRECT pATAData, bool write, BYTE* outBuff, int sizeBuff, int nbSectors, int sectorOffset, CHS sectorNum, UCHAR command, bool useDMA)
{
    //http://msdn.microsoft.com/en-us/library/windows/hardware/ff551322%28v=vs.85%29.aspx

    //Clear the contents
    ZeroMemory(pATAData, sizeof(ATA_PASS_THROUGH_DIRECT));

    // Init input / output buffs
    ZeroMemory(outBuff, sizeBuff);

    //Fill in query data
    pATAData->Length                = sizeof(ATA_PASS_THROUGH_DIRECT);
    pATAData->AtaFlags              = (write ? ATA_FLAGS_DATA_OUT : ATA_FLAGS_DATA_IN) | (useDMA ? ATA_FLAGS_USE_DMA : 0) | ATA_FLAGS_DRDY_REQUIRED | (nbSectors == 1 ? ATA_FLAGS_NO_MULTIPLE : 0);
    pATAData->PathId                = 0;        // Set by the port driver
    pATAData->TargetId              = 0;        // Set by the port driver
    pATAData->Lun                   = 0;        // Set by the port driver
    pATAData->DataTransferLength    = sizeBuff;
    pATAData->TimeOutValue          = 5;        //Seconds
    pATAData->DataBuffer            = outBuff;

    // http://msdn.microsoft.com/en-us/library/windows/hardware/ff559015%28v=vs.85%29.aspx
    PIDEREGS pIdeRegs = (PIDEREGS) &pATAData->CurrentTaskFile[0];

    pIdeRegs->bFeaturesReg      = 0x0;							    //Feature ID
    pIdeRegs->bSectorCountReg   = LOBYTE(LOWORD(nbSectors);	    	            //Number of sectors
    pIdeRegs->bSectorNumberReg  = LOBYTE(LOWORD(sectorNum.Sector));
    pIdeRegs->bCylLowReg        = LOBYTE(LOWORD(sectorNum.Cylinder));
    pIdeRegs->bCylHighReg       = HIBYTE(LOWORD(sectorNum.Cylinder));
    pIdeRegs->bDriveHeadReg     = LOBYTE(LOWORD(sectorNum.Head));
    pIdeRegs->bCommandReg       = command;						    //Command
    pIdeRegs->bReserved         = 0x0;							    //Res
}
EDIT: Also, I found that documentation: ftp://ftp.t10.org/t10/document.04/04-262r8.pdf
How am I supposed to pass that CDB block? Is it the same as CurrentTaskFile?
 #24883  by EP_X0FF
 Fri Jan 09, 2015 10:45 am
Do you understrand with current hardware + newest Windows installed in EFI mode all legacy BIOS bootkits are no longer work? And after few years computers with BIOS you can only see in the scrapyard?
Code: Select all
#include <windows.h>
#include "prtl.h"

#define IOCTL_SCSI_BASE                 FILE_DEVICE_CONTROLLER
#define FILE_DEVICE_CONTROLLER          0x00000004
#define IOCTL_ATA_PASS_THROUGH_DIRECT   CTL_CODE(IOCTL_SCSI_BASE, 0x040c, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#define ATA_FLAGS_DRDY_REQUIRED         (1 << 0)
#define ATA_FLAGS_DATA_IN               (1 << 1)
#define ATA_FLAGS_DATA_OUT              (1 << 2)
#define ATA_FLAGS_48BIT_COMMAND         (1 << 3)
#define ATA_FLAGS_USE_DMA               (1 << 4)
#define ATA_FLAGS_NO_MULTIPLE           (1 << 5)

typedef struct _ATA_PASS_THROUGH_DIRECT {
    USHORT Length;
    USHORT AtaFlags;
    UCHAR PathId;
    UCHAR TargetId;
    UCHAR Lun;
    UCHAR ReservedAsUchar;
    ULONG DataTransferLength;
    ULONG TimeOutValue;
    ULONG ReservedAsUlong;
    PVOID DataBuffer;
    UCHAR PreviousTaskFile[8];
    UCHAR CurrentTaskFile[8];
} ATA_PASS_THROUGH_DIRECT, *PATA_PASS_THROUGH_DIRECT;

#define INBUFFER_SIZE 1024*1024

int IsFileInfested(LPCTSTR FileName, LPVOID RawData, DWORD BufferSize)
{
	ATA_PASS_THROUGH_DIRECT		dio, dioOut;
	STARTING_VCN_INPUT_BUFFER	base;
	RETRIEVAL_POINTERS_BUFFER	*ptrs;
	VOLUME_DISK_EXTENTS			ext;
	LARGE_INTEGER				ofs, lofs;
	__int64						i, k;

	HANDLE	f = INVALID_HANDLE_VALUE;
	DWORD	iobytes, SectorsPerCluster = 0, BytesPerSector = 0, c, p = 0;
	TCHAR	drive[8] = TEXT("\\\\.\\X:\\");
	WORD    DevId[256] = {0};
	USHORT  AtaFlags;
	UCHAR   AtaCommand;

	drive[4] = FileName[0];
	if ( !GetDiskFreeSpace(&drive[4], &SectorsPerCluster, &BytesPerSector, NULL, NULL) )
		return -1;

	ptrs = (RETRIEVAL_POINTERS_BUFFER *)VirtualAlloc(NULL, INBUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
	while ( ptrs != NULL ) {
		f = CreateFile(FileName, GENERIC_READ | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
		if ( f == INVALID_HANDLE_VALUE )
			break;
		base.StartingVcn.QuadPart = 0;
		if ( !DeviceIoControl(f, FSCTL_GET_RETRIEVAL_POINTERS, &base, sizeof(base), ptrs, INBUFFER_SIZE, &iobytes, NULL) )
			break;
		CloseHandle(f);

		drive[6] = 0;
		f = CreateFile(drive, GENERIC_READ | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
		if ( f == INVALID_HANDLE_VALUE )
			break;
		if ( !DeviceIoControl(f, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &ext, sizeof(ext), &iobytes, NULL) )
			break;
		CloseHandle(f);

		f = CreateFile(TEXT("\\\\.\\PHYSICALDRIVE0"), GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
		if ( f == INVALID_HANDLE_VALUE )
			break;

		ext.Extents[0].StartingOffset.QuadPart /= BytesPerSector;
		lofs.QuadPart = 0;

		memset(&DevId, 0, sizeof(DevId));
		memset(&dio, 0, sizeof(dio));
		dio.Length = sizeof(dio);
		dio.AtaFlags = ATA_FLAGS_DRDY_REQUIRED | ATA_FLAGS_DATA_IN;
		dio.DataTransferLength = 512;
		dio.TimeOutValue = 1;
		dio.DataBuffer = &DevId;
		dio.CurrentTaskFile[6] = 0xEC;
		DeviceIoControl(f, IOCTL_ATA_PASS_THROUGH_DIRECT, &dio, sizeof(dio), &dioOut, sizeof(dioOut), &iobytes, NULL);

		if ((DevId[83] & 0x400) != 0) {
			AtaCommand = 0x25;
			AtaFlags = ATA_FLAGS_DRDY_REQUIRED | ATA_FLAGS_USE_DMA | ATA_FLAGS_DATA_IN | ATA_FLAGS_48BIT_COMMAND;
		} else {
			AtaCommand = 0xC8;
			AtaFlags = ATA_FLAGS_DRDY_REQUIRED | ATA_FLAGS_USE_DMA | ATA_FLAGS_DATA_IN;
		}


		for (c=0; c<ptrs->ExtentCount; c+=1) {
			ofs.QuadPart = ext.Extents[0].StartingOffset.QuadPart + (ptrs->Extents[c].Lcn.QuadPart*SectorsPerCluster);
			k = ptrs->Extents[c].NextVcn.QuadPart - lofs.QuadPart;

			if ( p+(BytesPerSector*SectorsPerCluster) > BufferSize )
				break;

			for (i=0; i<k; i+=1) {
				if ( p+(BytesPerSector*SectorsPerCluster) > BufferSize )
					break;
				lofs.QuadPart = ofs.QuadPart + i*SectorsPerCluster;

				memset(&dio, 0, sizeof(dio));
				dio.Length = sizeof(dio);
				dio.AtaFlags = AtaFlags;
				dio.DataTransferLength = BytesPerSector*SectorsPerCluster;
				dio.TimeOutValue = 1;
				dio.DataBuffer = ((LPBYTE)RawData)+p;

				dio.PreviousTaskFile[2] = (lofs.QuadPart >> 24) & 0xff;
				dio.PreviousTaskFile[3] = (lofs.QuadPart >> 32) & 0xff;
				dio.PreviousTaskFile[4] = (lofs.QuadPart >> 40) & 0xff;

				dio.CurrentTaskFile[1] = (UCHAR)SectorsPerCluster;
				dio.CurrentTaskFile[2] = lofs.QuadPart & 0xff;
				dio.CurrentTaskFile[3] = (lofs.QuadPart >> 8) & 0xff;
				dio.CurrentTaskFile[4] = (lofs.QuadPart >> 16) & 0xff;
				
				if (AtaCommand == 0xC8) {
					dio.CurrentTaskFile[5] = 0x40 | ((lofs.QuadPart >> 24) & 0x0f);
				} else {
					dio.CurrentTaskFile[5] = 0x40;
				}
				
				dio.CurrentTaskFile[5] = 0x40;

				dio.CurrentTaskFile[6] = AtaCommand;
				DeviceIoControl(f, IOCTL_ATA_PASS_THROUGH_DIRECT, &dio, sizeof(dio), &dioOut, sizeof(dioOut), &iobytes, NULL);
				p+=(BytesPerSector*SectorsPerCluster);
			}
			lofs.QuadPart = ptrs->Extents[c].NextVcn.QuadPart;
		}
		CloseHandle(f);

		VirtualFree(ptrs, 0, MEM_RELEASE);
		return 0;
	}

	if ( f != INVALID_HANDLE_VALUE )
		CloseHandle(f);
	if ( ptrs != NULL )
		VirtualFree(ptrs, 0, MEM_RELEASE);
	return -1;
}

#define sFileName TEXT("C:\\WINDOWS\\system32\\drivers\\fltmgr.sys")

void main()
{
	LPVOID buffer;
	HANDLE	f;
	DWORD	iobytes, fsize = 0;

	

	f = CreateFile(sFileName, GENERIC_READ | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
	if ( f != INVALID_HANDLE_VALUE ) {
		fsize = GetFileSize(f, NULL);
		CloseHandle(f);
	}

	buffer = VirtualAlloc(NULL, fsize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
	memset(buffer, 0xcc, fsize);

	IsFileInfested(sFileName, buffer, fsize);
	f = CreateFile(TEXT("Z:\\TEMP\\4321.dmp"), GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
	if (f != INVALID_HANDLE_VALUE) {
		WriteFile(f, buffer, fsize, &iobytes, NULL);
		CloseHandle(f);
	}
	Sleep(0);
}
 #24885  by Tigzy
 Fri Jan 09, 2015 11:12 am
Thanks EP_X0FF, I know that :) Currently it's just for educational purpose. I had that code working because as I said I was checking only the MBR.
Now for the sake of consistency I'd like to fix it for a general usage.
Code: Select all
dio.CurrentTaskFile[6] = 0xEC;
=> What is that for?
What is the first call for, nothign set except the field above?
 #24886  by EP_X0FF
 Fri Jan 09, 2015 11:16 am
Tigzy wrote:
Code: Select all
dio.CurrentTaskFile[6] = 0xEC;
=> What is that for?
What is the first call for, nothign set except the field above?
It is ATA device idenfity command.
 #24888  by Tigzy
 Fri Jan 09, 2015 12:16 pm
Actually it's kinda work, but I have a very strange problem.
When I read one sector, it's fine. But when I want to read 2 sectors, the system adds some 'padding' between the 2 sectors' data, like that:

EDIT: I'm on XP VirtualBox
Capture.PNG
Capture.PNG (141.81 KiB) Viewed 620 times
 #24996  by Tigzy
 Mon Jan 19, 2015 10:25 am
An important thing I discovered, if the sector count is higher than 4096 bytes, *sometimes* (I haven't found why) the DeviceIOControl fails with error ERROR_NOACCESS.
Looks like I'm not the only one to have the issue: https://social.msdn.microsoft.com/Forum ... latformata

But easily fixed by "chuncking" the call into smaller sub-calls and put all together in the end.
 #24999  by EP_X0FF
 Mon Jan 19, 2015 12:13 pm
If you mean his code, then I really wonder why it work sometimes, not why it don't work sometimes with all his alignment fuckups and mathematics resulting in jumping to the guard page ROFL.