A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #10052  by Tigzy
 Thu Dec 01, 2011 10:40 am
Finally the french wiki does the trick (READ 10 -> http://fr.wikipedia.org/wiki/Commande_SCSI#Read_10)
Here's the code I wrote:
Code: Select all
GetSPTIMBR(mbrBuff /*512 bytes sized*/, 512, 0 /*PhysicalDrive0*/)

...

bool GetSPTIMBR(byte* outBuff, int sizeBuff, int phyDrive)
{   
	DWORD outBytes;
	SCSI_PASS_THROUGH_WITH_BUFFERS sptwb;
    SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptdwb;
	ULONG length = 0;

	// Init drive name
	wstring fileName = L"\\\\.\\PhysicalDrive";
	WCHAR nDrive[3];
	_stprintf_s(nDrive, L"%d", phyDrive);
	fileName.append(nDrive);

	// Open device
	HANDLE hDevice = CreateFile(fileName.c_str(), GENERIC_WRITE | GENERIC_READ , FILE_SHARE_READ | FILE_SHARE_WRITE , NULL, OPEN_EXISTING, NULL, NULL);    
	if (hDevice == INVALID_HANDLE_VALUE)
	{
		return (FALSE);
	}
	
	// Init input / output buffs
	ZeroMemory(&sptdwb, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER));
    ZeroMemory(outBuff,sizeBuff);

	// Init struct
	sptdwb.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
	//sptdwb.sptd.ScsiStatus = 0; // Must be 0 --> filled by the function
    sptdwb.sptd.PathId = 0; // Id of the SCSI bus adapter , often 0
    sptdwb.sptd.TargetId = 1; // Scsi Id number of the device
    sptdwb.sptd.Lun = 0; // Logical unit number of the device
    sptdwb.sptd.CdbLength = CDB10GENERIC_LENGTH; // Size of the CDB struct
    sptdwb.sptd.DataIn = SCSI_IOCTL_DATA_IN; // Size of the data
    sptdwb.sptd.SenseInfoLength = SPT_SENSE_LENGTH; // Length of the SenseInfo buff
    sptdwb.sptd.DataTransferLength = sizeBuff; // Sector size
    sptdwb.sptd.TimeOutValue = 2; // Timeout (secs)
    sptdwb.sptd.DataBuffer = outBuff; // Pointer to dataBuff
    sptdwb.sptd.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER,ucSenseBuf);

	// Init CDB --> http://fr.wikipedia.org/wiki/Commande_SCSI
    sptdwb.sptd.Cdb[0] = SCSIOP_READ; //SCSIOP_READ_DATA_BUFF;
    sptdwb.sptd.Cdb[1] = 0x04; //  LUN = 000b, DPO = 0, FUA = 1, RFU1 = 0, RFU1 = 0, RelAdr = 0
	sptdwb.sptd.Cdb[2] = 0x00; //    Cdb[2-->5] = 0;   //  First sector Logical block addresss 
	sptdwb.sptd.Cdb[3] = 0x00;
	sptdwb.sptd.Cdb[4] = 0x00;
	sptdwb.sptd.Cdb[5] = 0x00;
	sptdwb.sptd.Cdb[6] = 0x00; //    Cdb[6] = 0;   //  RFU	

	byte size[2];
	convertIntTo2bytes (sizeBuff, size); // convert int into LSB / MSB 

        sptdwb.sptd.Cdb[7] = size[1]; //MSB
        sptdwb.sptd.Cdb[8] = size[0]; //LSB
	

	length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
	bool status = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptdwb, length, &sptdwb, length, &outBytes, FALSE);

	CloseHandle(hDevice);
	return true;
}
 #10173  by Tigzy
 Tue Dec 06, 2011 9:42 am
Hello

Found another clue to read MBR in memory.

According to this: http://geezer.osdevbrasil.net/osd/ram/index.htm
Bootsectors and MBRs are loaded at addresses 7C00h - 7DFFh. (in physical memory) Depending on how it's written, this code may use additional memory outside this range. The DOS 7 MBR copies itself to 600h - 7FFh, leaving 7C00h - 7DFFh free for successive MBRs or bootsectors.
Based on this: http://www.codeproject.com/KB/system/so ... _hack.aspx
I developed a code to read the physical memory and find the MBR. Seems to work, but the I must be at the wrong adress... Anyone got knowledge about physical memory mapping and reading?
Code: Select all
bool GetPhyMemMBR(byte* outBuff, int sizeBuff, int phyDrive)
{
	// Open handle
	HANDLE hMem = OpenMemAccess();		

	// Map memory
	DWORD size = 4096; /*SECTION SIZE*/
	DWORD pPhyAddr = 0x7c00; /*MAGIC NUMBER*/
	ULONG Base = 0x0;
	int ret = MapMem(hMem, &Base, &pPhyAddr, &size);

	//---- Read memory ----
	DWORD outBytes;
	if (!ReadProcessMemory( GetCurrentProcess(), &Base,   outBuff,  sizeBuff, &outBytes))
	{
		printf ("Error : %d\n", GetLastError());
	}
	
	// Unmap memory
	ret = UnMapMem(&Base);
	
	// Close handle
	CloseMemAccess(hMem);

	return true;
}

Code: Select all
int MapMem(HANDLE hPhysMem, PVOID pBaseAddr, PDWORD pPhysAddr, PDWORD pSize)
{
    NTSTATUS status;
    LARGE_INTEGER paAddr;
 
    * (DWORD *) pBaseAddr = (DWORD) NULL;
	paAddr.HighPart = 0;
	paAddr.LowPart = *pPhysAddr;
    status = NtMapViewOfSection(hPhysMem, GetCurrentProcess(), (PVOID *) pBaseAddr, 0L, *pSize, &paAddr, pSize, ViewShare, 0, PAGE_READONLY);
 
    if (status != ERROR_SUCCESS)
    {
        hPhysMem = NULL;
        return FALSE;
    }
 
	*pPhysAddr = paAddr.LowPart;
    return TRUE;
}
 #10175  by Vrtule
 Tue Dec 06, 2011 11:04 am
Hello,

NtMapViewOfSection (or MapViewOfFile(Ex)) maps memory regions at 64 KB of granularity. Tis means that when you attempt to map physical region starting from 0x7C000, the mapping actually starts at 0x70000.

This holds for mappings of disk files. And I think it should hold for other mappings too.

And there some ugly things in your code (comparing NTSTATUS values with ERROR_SUCCESS, for example. And more).
 #10179  by Tigzy
 Tue Dec 06, 2011 1:49 pm
Thanks for replying.

I definitely misunderstanding something :/
Code: Select all
// Map memory
DWORD size = 0x10000; /*GRANULARITY SIZE*/
DWORD pPhyAddr = 0x0000; /*MAGIC NUMBER*/
ULONG Base = 0x0;

Reading the whole 0x10000 ; everything empty...
 #10182  by Tigzy
 Tue Dec 06, 2011 3:08 pm
Have found this copy of MBR, but in virtualbox it's been partially erased at the very beginning.
On a physical PC, the MBR is still there

The offset is not 0x7C00 , but 0x600, as described here: http://geezer.osdevbrasil.net/osd/ram/index.htm
Depending on how it's written, this code may use additional memory outside this range. The DOS 7 MBR copies itself to 600h - 7FFh, leaving 7C00h - 7DFFh free for successive MBRs or bootsectors.
Sans titre 1.png
Sans titre 1.png (31.45 KiB) Viewed 337 times
 #10191  by Dmitry Varshavsky
 Tue Dec 06, 2011 7:48 pm
Tigzy, let me try to clarify your efforts:
1. Initially MBR is loaded by the BIOS at 0x7C00
2. Typical MBR immediately relocates itself to 0x0600. For example, TDL4 boot sector begin with the following code:
Code: Select all
XOR AX, AX
MOV SS, AX
MOV SP, 0x7c00
MOV ES, AX
MOV DS, AX
MOV SI, 0x7c00
MOV DI, 0x600
MOV CX, 0x200
CLD
REP MOVSB
PUSH AX
PUSH 0x61c
RETF
which relocates MBR to 0x600. Note, MBR, especially when we talk about modified MBR, may be relocated to *any* address. I didn't check on infected machine, but memory at 0x600 may be easily zeroed or clean copy of MBR may be placed there.

So, the main points are:
a) nobody guarantee that mrb code will be relocated to 0x600 at boot time ( usually it is )
b) nobody guarantee that memory @ 0x600 is not overwritten

As for me, I would not use this method to detect anything, but it was a very good try. Keep going !
 #10192  by Tigzy
 Tue Dec 06, 2011 8:01 pm
b) nobody guarantee that memory @ 0x600 is not overwritten
You're right, after several tries: 2 VMs under XP are partially erased , 1 physical PC was totally erased , 1 is completely available. Not reliable... :|
Moreover, my code not working under Vista / Seven. I haven't time to check why, but I guess this is a problem with PhysicalMemory device access.

If someone got others pointers to me...
 #10199  by Vrtule
 Tue Dec 06, 2011 10:03 pm
You're right, after several tries: 2 VMs under XP are partially erased , 1 physical PC was totally erased , 1 is completely available. Not reliable... :|
Moreover, my code not working under Vista / Seven. I haven't time to check why, but I guess this is a problem with PhysicalMemory device access.
On Windows Vista and 7, \Device\PhysicalMemory cannot be opened from usermode. Maybe, this behaviour starts on Windows Server 2003 with certain Service Pack, but I am not sure about it.

I have coded a small driver that allows me to map and read physical memory even when this is not possible from usermodode. For testing purposes, of course. If you are interested, I can share it at the weekend.
  • 1
  • 3
  • 4
  • 5
  • 6
  • 7