Tuesday, April 23, 2013

Deep Dive to Windows, Part 4



Launched a process sample_Hello through WinDbg and observed following events:

ModLoad: 00400000 0041c000   Sample_Hello.exe
ModLoad: 77980000 77b00000   ntdll.dll
ModLoad: 76b10000 76c20000   C:\Windows\syswow64\kernel32.dll
ModLoad: 76d20000 76d67000   C:\Windows\syswow64\KERNELBASE.dll
ModLoad: 67fd0000 680ce000   C:\Windows\WinSxS\x86_microsoft.vc80.debugcrt_1fc8b3b9a1e18e3b_8.0.50727.6195_none_e4a70117006762dd\MSVCP80D.dll
ModLoad: 670e0000 67201000   C:\Windows\WinSxS\x86_microsoft.vc80.debugcrt_1fc8b3b9a1e18e3b_8.0.50727.6195_none_e4a70117006762dd\MSVCR80D.dll
ModLoad: 77050000 770fc000   C:\Windows\syswow64\msvcrt.dll
(3c0.1324): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=fb480000 edx=0008e3c8 esi=fffffffe edi=00000000
eip=77a20fab esp=0018fb08 ebp=0018fb34 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!LdrpDoDebuggerBreak+0x2c:
77a20fab cc              int     3

Process launched but execution halted with a Break instruction exception (please see highlighted line above). Why this happened?

This is due to the fact called “breaking into the debugger”.  We will see this later. Now let’s see how many threads are there in this state. I’m expecting 1 since my application is a single threaded application.

So, I’ve executed the command ~* on windbg.

0:000> ~*
.  0  Id: 3c0.1324 Suspend: 1 Teb: 7efdd000 Unfrozen
      Start: *** WARNING: Unable to verify checksum for Sample_Hello.exe
Sample_Hello!ILT+145(_wmainCRTStartup) (00411096)
      Priority: 0  Priority class: 32  Affinity: 3

Indeed I’ve a thread.  Let’s see the stack of the thread:

0:000> ~*kb

.  0  Id: 3c0.1324 Suspend: 1 Teb: 7efdd000 Unfrozen
ChildEBP RetAddr  Args to Child             
0018fb34 77a01383 7efdd000 7efde000 77a8206c ntdll!LdrpDoDebuggerBreak+0x2c
0018fcb0 779c52d6 0018fd24 77980000 7b14b7c0 ntdll!LdrpInitializeProcess+0x12cc
0018fd00 779b9e79 0018fd24 77980000 00000000 ntdll!_LdrpInitialize+0x78
0018fd10 00000000 0018fd24 77980000 00000000 ntdll!LdrInitializeThunk+0x10

So, after initialize process call there is a DebuggerBreak call.

Now I’ve executed the ‘g’ (go) and debugee will start running. Lets’s see:

Yes, Debugee is in running state:
Debuggee is running...

Since my program uses for user input to terminate so program is waiting for input. It is going to help me to see the state of the process.

Again I’ve used debugger break point to halt the application and now would like to see how many threads are there:

0:001> ~*
   0  Id: 3c0.1324 Suspend: 1 Teb: 7efdd000 Unfrozen
      Start: Sample_Hello!ILT+145(_wmainCRTStartup) (00411096)
      Priority: 0  Priority class: 32  Affinity: 3
.  1  Id: 3c0.14c4 Suspend: 1 Teb: 7efda000 Unfrozen
      Start: ntdll!DbgUiRemoteBreakin (77a1f85a)
      Priority: 0  Priority class: 32  Affinity: 3

Now we’ve two threads but my application is a single threaded application, so what’s the other thread?

Let’s see the stack:

   0  Id: 3c0.1324 Suspend: 1 Teb: 7efdd000 Unfrozen
ChildEBP RetAddr  Args to Child             
0018fa24 76bc7379 00000003 671f4aa0 00001000 kernel32!ReadConsoleInternal+0x15
0018faac 76b4f1b2 00000003 671f4aa0 00001000 kernel32!ReadConsoleA+0x40
0018faf4 67151b6a 00000003 671f4aa0 00001000 kernel32!ReadFileImplementation+0x75
0018fb88 671514c7 00000000 671f4aa0 00001000 MSVCR80D!_read_nolock+0x62a [f:\dd\vctools\crt_bld\self_x86\crt\src\read.c @ 233]
0018fbd8 67177da1 00000000 671f4aa0 00001000 MSVCR80D!_read+0x217 [f:\dd\vctools\crt_bld\self_x86\crt\src\read.c @ 93]
0018fc00 67180a7b 671f1d08 489faa4c 0018ff30 MSVCR80D!_filbuf+0x111 [f:\dd\vctools\crt_bld\self_x86\crt\src\_filbuf.c @ 136]
0018fc54 67fe6b2e 671f1d08 0018fd3f 0018fd4c MSVCR80D!fgetc+0x24b [f:\dd\vctools\crt_bld\self_x86\crt\src\fgetc.c @ 49]
0018fc64 67fe67c5 0018fd3f 671f1d08 489daae2 MSVCP80D!std::_Fgetc+0xe [f:\dd\vctools\crt_bld\self_x86\crt\src\fstream @ 37]
0018fd4c 67fe66ce 0018fe58 680c4950 00000000 MSVCP80D!std::basic_filebuf >::uflow+0xb5 [f:\dd\vctools\crt_bld\self_x86\crt\src\fstream @ 362]
0018fd64 67fe1018 00000000 680c4950 0018fdd0 MSVCP80D!std::basic_filebuf >::underflow+0x4e [f:\dd\vctools\crt_bld\self_x86\crt\src\fstream @ 341]
0018fd74 67fe1cb2 489daa7e 0018ff30 0018fe58 MSVCP80D!std::basic_streambuf >::sgetc+0x38 [f:\dd\vctools\crt_bld\self_x86\crt\src\streambuf @ 113]
0018fdd0 67fe0f57 00001300 489daa5e 0018fe2c MSVCP80D!std::basic_istream >::_Ipfx+0x102 [f:\dd\vctools\crt_bld\self_x86\crt\src\istream @ 113]
0018fdf0 68040f31 680c49a4 00000000 489da9e6 MSVCP80D!std::basic_istream >::sentry::sentry+0x47 [f:\dd\vctools\crt_bld\self_x86\crt\src\istream @ 86]
0018fe48 004115e6 680c49a4 0018ff2b 00000000 MSVCP80D!std::operator>> >+0x41 [f:\dd\vctools\crt_bld\self_x86\crt\src\istream @ 997]
0018ff30 004125b6 00000001 007d4778 007d57f0 Sample_Hello!wmain+0x66 [f:\fordebugging\sample_hello\sample_hello\sample_hello.cpp @ 28]
0018ff80 004123fd 0018ff94 76b233aa 7efde000 Sample_Hello!__tmainCRTStartup+0x1a6 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 594]
0018ff88 76b233aa 7efde000 0018ffd4 779b9ef2 Sample_Hello!wmainCRTStartup+0xd [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 414]
0018ff94 779b9ef2 7efde000 7b14b514 00000000 kernel32!BaseThreadInitThunk+0xe
0018ffd4 779b9ec5 00411096 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70
0018ffec 00000000 00411096 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b

#  1  Id: 3c0.14c4 Suspend: 1 Teb: 7efda000 Unfrozen
ChildEBP RetAddr  Args to Child             
0051ff58 77a1f896 7b5db548 00000000 00000000 ntdll!DbgBreakPoint
0051ff88 76b233aa 00000000 0051ffd4 779b9ef2 ntdll!DbgUiRemoteBreakin+0x3c
0051ff94 779b9ef2 00000000 7b5db514 00000000 kernel32!BaseThreadInitThunk+0xe
0051ffd4 779b9ec5 77a1f85a 00000000 00000000 ntdll!__RtlUserThreadStart+0x70
0051ffec 00000000 77a1f85a 00000000 00000000 ntdll!_RtlUserThreadStart+0x1b

Thread 0 stack is known to me but what’s the thread 1. Actually the thread 1 is the debugger thread. This injects this (thread 1) thread to target process as part of break in command.  So the call was:

0051ff88 76b233aa 00000000 0051ffd4 779b9ef2 ntdll!DbgUiRemoteBreakin+0x3c

Since thread 1 is the active thread (marked with ‘.’) and ntdll!DbgBreakPoint is the last call in the stack, lets unassembled it:

0:001> uf ntdll!DbgBreakPoint
ntdll!DbgBreakPoint:
7799000c cc              int     3
7799000d c3              ret

Got it; this calls INT 3 (Interrupt 3). Execution freezes here. So we’ve used the term “breaking into the debugger” at the beginning. It says that user mode debugger can interrupt at any point of time to freeze the target process.  In response to this interrupt, OS raises a structured exception (Windows SEH) in the context of break-in thread and as result, first chance exception occurred with exception code 80000003 (first chance).

Happy Debugging...

Thursday, April 11, 2013

Detect Antivirus installed on Windows 7

In this article, I've tried to show how we can detect antivirus product installed on a Windows system. The code is written is specifically for Windows 7.

The basic idea here is to use WMI from C++. Here are the steps:

1. To Setup WMI consumer, set up COM by calling CoInitializeEx.

2. Initialized COM process security by calling CoInitializeSecurity.

3. Obtained the initial locator to WMI by calling CoCreateInstance.

4. Obtained a pointer to IWbemServices for the root\cimv2 namespace on the local computer by calling IWbemLocator::ConnectServer.

5. Set IWbemServices proxy security so the WMI service can impersonate the client by calling CoSetProxyBlanket.

6.Used the IWbemServices pointer to make requests of WMI. This executes a WQL query for the antivirus product installed by calling IWbemServices::ExecQuery.
The following WQL query is one of the method arguments.
SELECT * FROM AntiVirusProduct

The result of this query is stored in an IEnumWbemClassObject pointer. This allows the data objects from the query to be retrieved semi-synchronously with the IEnumWbemClassObject interface.

7.Get and display the data from the WQL query. The IEnumWbemClassObject pointer is linked to the data objects that the query returned, and the data objects can be retrieved with the IEnumWbemClassObject::Next method. This method links the data objects to an IWbemClassObject pointer that is passed into the method. Used IWbemClassObject::GetObjectText method to get the desired information from the data objects.

The following code reveals Antivirus installed on Windows 7:

#include
using namespace std;
#include
#include

#pragma comment(lib, "wbemuuid.lib")

int main(int iArgCnt, char ** argv)
{
    HRESULT hres;

    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------

    hres =  CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres))
    {
        cout << "Failed to initialize COM library. Error code = 0x"
             << hex << hres << endl;
        return 1;                  // Program has failed.
    }

    // Step 2: --------------------------------------------------
    // Set general COM security levels --------------------------
    // Note: If you are using Windows 2000, you must specify -
    // the default authentication credentials for a user by using
    // a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
    // parameter of CoInitializeSecurity ------------------------

    hres =  CoInitializeSecurity(
        NULL,
        -1,                          // COM negotiates service
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
        RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation 
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities
        NULL                         // Reserved
        );

                     
    if (FAILED(hres))
    {
        cout << "Failed to initialize security. Error code = 0x"
             << hex << hres << endl;
        CoUninitialize();
        return 1;                      // Program has failed.
    }
   
    // Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------

    IWbemLocator *pLoc = NULL;

    hres = CoCreateInstance(
        CLSID_WbemLocator,            
        0,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator, (LPVOID *) &pLoc);

    if (FAILED(hres))
    {
        cout << "Failed to create IWbemLocator object. "<< "Err code = 0x" << hex << hres << endl;
        CoUninitialize();
        return 1;
    }

    // Step 4: ---------------------------------------------------
    // Connect to WMI through the IWbemLocator::ConnectServer method

    IWbemServices *pSvc = NULL;
   
    // Connect to the local root\SecurityCenter namespace
    // Please change the root\\SecurityCenter to root\\SecurityCenter2 on Windows 7
    // and obtain pointer pSvc to make IWbemServices calls.
    hres = pLoc->ConnectServer(
        _bstr_t(L"root\\SecurityCenter2"),
        NULL, NULL, 0, NULL, 0, 0, &pSvc
    );
       
    if (FAILED(hres))
    {
        cout << "Could not connect. Error code = 0x" << hex << hres << endl;
        pLoc->Release();    
        CoUninitialize();
        return 1;                // Program has failed.
    }

    cout << "Connected to ROOT\\SecurityCenter2 WMI namespace" << endl;

    hres = CoSetProxyBlanket(
       pSvc,                                                  // Indicates the proxy to set
       RPC_C_AUTHN_WINNT,             // RPC_C_AUTHN_xxx
       RPC_C_AUTHZ_NONE,                // RPC_C_AUTHZ_xxx
       NULL,                                                // Server principal name
       RPC_C_AUTHN_LEVEL_CALL,  // RPC_C_AUTHN_LEVEL_xxx
       RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
       NULL,                                                // client identity
       EOAC_NONE                                  // proxy capabilities
    );

    if (FAILED(hres))
    {
        cout << "Could not set proxy blanket. Error code = 0x"
            << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();    
        CoUninitialize();
        return 1;               // Program has failed.
    }

    // Step 5: ---------------------------------------------------
    // Query Security Centre for Antivirus Prodict installed------
   
    IEnumWbemClassObject *pEnumerator;

    hres = pSvc->ExecQuery(L"WQL", L"SELECT * FROM AntiVirusProduct",
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL, &pEnumerator );

    if (FAILED(hres))
    {
        cout << "Query for operating system name failed."
            << " Error code = 0x" << hex << hres << endl;

        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return 1;               // Program has failed.
    }

    ULONG retcnt = 0;
    IWbemClassObject *pAntivirus;
    while(pEnumerator)
    {
        HRESULT hr = pEnumerator->Next( WBEM_INFINITE, 1L, &pAntivirus, &retcnt );

        if(0 == retcnt)
        {
            break;
        }

        BSTR objText;
        pAntivirus->GetObjectText(0, &objText);
        _bstr_t bstrStr(objText);

        LPCSTR str = bstrStr;
        cout << str << endl;

        ::SysFreeString(bstrStr);
    }
   
    pSvc->Release();
    pLoc->Release();
    pEnumerator->Release();
    pAntivirus->Release();
    CoUninitialize();
    return 0;
}


The output:

Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/aa390423(v=vs.85).aspx

Monday, April 8, 2013

About Windows Executable File Size

This post is basically an effort to know what happens when we compile a simple Hello World program on Windows. In this case I've used Visual Studio 2005.

First I've written a small usual Hello World program with all default settings provided by Visual Studio 2005.

The program looks like below:

int main(void)
{
    printf("Hello World build on VS 2005--Default\n");
    return 0;
}

The program compiled against default C-runtime library. No changes has been done in any settings. I've build it in Debug as well as release mode. In Debug build, the program size is: 40 KB. In release build: It's size is 6 KB.

Now I've written another Hello World Progarm but switched off default C-Runtime library. Rather, I've used standard windows library and provided definition of functions like printf as well as CRT start up function. I've also switched of Buffer Security Check and Basic Runtime checks set to Default. The last two settings being enabled to avoid linking issue since I'm not going to use standard C runtime library.

So the code looks like below:

extern "C" int __cdecl printf(const char * format, ...)
{
    char szBuff[1024];
    int retValue;
    DWORD cbWritten;
    va_list argptr;
         
    va_start( argptr, format );
    retValue = wvsprintfA( szBuff, format, argptr );
    va_end( argptr );

    WriteFile(  GetStdHandle(STD_OUTPUT_HANDLE), szBuff, retValue, &cbWritten, 0 );

    return retValue;
}

int main(void)
{
    printf ("Hello World build on VS 2005\n");
    return 0;
}

int mainCRTStartup() // This my CRT start up finction which in turn invokes main
{
    int retval;
   
    retval = main();
    ExitProcess(retval);
}

I've build this in Debug as well as release mode. And amazingly the size reduced to 24 KB in debug (16 KB less than default one) and 3 KB in release (3 KB less than the default one as mentioned before). However, both program shows same output.

This is one of the thing Matt Pietrek has shown in his well known article "Under the hood" (http://msdn.microsoft.com/library/bb985746.aspx).

Note: The printf function has been adapted from Matt's article.

Thursday, April 4, 2013

HDD enumeration and info retreive - another way

In this part I tried to enumerate all physical hard disk drive (HDD) attached to the system and tried to query to those attached physical drive to get the disk information like Vendor ID, Product ID, Product Revision, Serial number etc.

In my last blog, I've tried to get physical hard disk drive count through volume map, but in this post, I tried get it through "SetupDiGetClassDevs" API. All the SetupDiXXX APIs are very powerful APIs. These APIs along with DeviceIoControl API helps to retrieve very useful information regarding devices. So, I'm not going to talk much on this rather let MSDN to speak about this APIs.

Let's see what are other information that we can get on HDD attached to the system through the usage of this API:

void printStorageDeviceProperty(UCHAR *outBuf, const DWORD returnedLength)
{
    PSTORAGE_DEVICE_DESCRIPTOR            devDesc;
    PUCHAR                              pUbuffer;

    devDesc = (PSTORAGE_DEVICE_DESCRIPTOR) outBuf;
           
    pUbuffer = (PUCHAR) outBuf;
    if ( devDesc->VendorIdOffset && pUbuffer[devDesc->VendorIdOffset] )
    {
        wprintf(L"Vendor ID       : " );
        for ( DWORD i = devDesc->VendorIdOffset; pUbuffer[i] != (UCHAR) NULL && i < returnedLength; i++ )
        {
            wprintf( L"%c", pUbuffer[i] );
        }
        wprintf(L"\n");
    }

    if ( devDesc->ProductIdOffset && pUbuffer[devDesc->ProductIdOffset] )
    {
        wprintf(L"Product ID       : " );
        for ( DWORD i = devDesc->ProductIdOffset; pUbuffer[i] != (UCHAR) NULL && i < returnedLength; i++ )
        {
            wprintf( L"%c", pUbuffer[i] );
        }
        wprintf(L"\n");
    }

    if ( devDesc->ProductRevisionOffset && pUbuffer[devDesc->ProductRevisionOffset] )
    {
        wprintf(L"Product Revision       : " );
        for ( DWORD i = devDesc->ProductRevisionOffset; pUbuffer[i] != (UCHAR) NULL && i < returnedLength; i++ )
        {
            wprintf( L"%c", pUbuffer[i] );
        }
        wprintf(L"\n");
    }

    if ( devDesc->SerialNumberOffset && pUbuffer[devDesc->SerialNumberOffset] )
    {
        wprintf(L"Serial Number       : " );
        for ( DWORD i = devDesc->SerialNumberOffset; pUbuffer[i] != (UCHAR) NULL && i < returnedLength; i++ )
        {
            wprintf( L"%c", pUbuffer[i] );
        }
        wprintf(L"\n");
    }
   
    wprintf(L"Removable Media : %s\n", ((devDesc->RemovableMedia) ? L"Yes..." : L"No..."));
}

void printMediaType(HANDLE hDevice)
{
    PGET_MEDIA_TYPES MediaTypes = {0};
    BOOL    status = FALSE;
    UCHAR   buffer[2048];
    ULONG    returnedLength;

    status = DeviceIoControl(hDevice, IOCTL_STORAGE_GET_MEDIA_TYPES_EX, NULL, 0, buffer, sizeof(buffer), &returnedLength, FALSE);

    if (!status)
    {
        wprintf(L"IOCTL_STORAGE_GET_MEDIA_TYPES_EX failed with error code%d.\n\n", GetLastError());
        return;
    }

    MediaTypes = (PGET_MEDIA_TYPES) buffer;
    switch(MediaTypes->DeviceType)
    {
        case FILE_DEVICE_DISK:
            wprintf(L"Media Type: Device Disk\n");
            break;
        case FILE_DEVICE_DISK_FILE_SYSTEM:
            wprintf(L"Media Type: Device Disk File System\n");
            break;
        case FILE_DEVICE_FILE_SYSTEM:
            wprintf(L"Media Type: File Device File System\n");
            break;
        default:
            wprintf(L"Media Type: Unknown");
            break;
    }

    // Device Media Info
    for (DWORD i = 0; i < MediaTypes->MediaInfoCount; i++)
    {
        wprintf(L"Bytes/Sector:        %ld\n", MediaTypes->MediaInfo[i].DeviceSpecific.DiskInfo.BytesPerSector);
        wprintf(L"No. of Cylinders: %I64d\n", MediaTypes->MediaInfo[i].DeviceSpecific.DiskInfo.Cylinders);
        // wprintf(L"Media Characteristics: %ld\n", MediaTypes->MediaInfo[i].DeviceSpecific.DiskInfo.MediaCharacteristics);
        switch(MediaTypes->MediaInfo[i].DeviceSpecific.DiskInfo.MediaType)
        {
        case FixedMedia:
            wprintf(L"Media Type:    FixedMedia\n");
            break;
        default:
            wprintf(L"Media Type:    Unknown...\n");
            break;
        }
        wprintf(L"No. of sides:        %ld\n", MediaTypes->MediaInfo[i].DeviceSpecific.DiskInfo.NumberMediaSides);
        wprintf(L"Sectors/track:    %ld\n", MediaTypes->MediaInfo[i].DeviceSpecific.DiskInfo.SectorsPerTrack);
        wprintf(L"Tracks/Cylinder:    %ld\n", MediaTypes->MediaInfo[i].DeviceSpecific.DiskInfo.TracksPerCylinder);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    HDEVINFO hDevInfo;
    SP_DEVINFO_DATA DeviceInfoData;
    DWORD i;

    // Create a HDEVINFO with all HDD present in system.
    hDevInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVINTERFACE_DISK,
       0, // Enumerator
       0, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE );

    if (hDevInfo == INVALID_HANDLE_VALUE)
    {
       return 1;
    }
   
    // Enumerate through all physical drive in Set.
    DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    for(i=0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); i++)
    {
        LPTSTR buffer = NULL;
        DWORD buffersize = 0;

        SP_DEVICE_INTERFACE_DATA                interfaceData;
        PSP_DEVICE_INTERFACE_DETAIL_DATA        interfaceDetailData = NULL;
        HANDLE                                    hDevice;
        BOOL                                    status;
        DWORD                                    interfaceDetailDataSize;
        DWORD                                    reqBufSize;
        DWORD                                    errorCode;

        interfaceData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);

        status = SetupDiEnumDeviceInterfaces (
            hDevInfo,                    // Interface Device Info handle
            0,                            // Device Info data
            (LPGUID)&DiskClassGuid,        // Interface registered by driver
            i,                            // Member
            &interfaceData                // Device Interface Data
        );

        status = SetupDiGetDeviceInterfaceDetail(hDevInfo, &interfaceData, NULL, 0, &reqBufSize, NULL);
        if(status == FALSE)
        {
            errorCode = GetLastError();
            if(errorCode != ERROR_INSUFFICIENT_BUFFER)
            {
                wprintf( L"SetupDiGetDeviceInterfaceDetail failed with error: %d\n", errorCode   );
                return FALSE;
            }
        }

        interfaceDetailDataSize = reqBufSize;
        interfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, reqBufSize);

        interfaceDetailData->cbSize = sizeof (SP_INTERFACE_DEVICE_DETAIL_DATA);

        status = SetupDiGetDeviceInterfaceDetail(hDevInfo, &interfaceData, interfaceDetailData,
            interfaceDetailDataSize, &reqBufSize, NULL);

        if ( status == FALSE )
        {
            wprintf(L"Error in SetupDiGetDeviceInterfaceDetail failed with error: %d\n", GetLastError());
            return FALSE;
        }

        wprintf( L"Interface: %s\n", interfaceDetailData->DevicePath);

        hDevice = CreateFile(
                interfaceDetailData->DevicePath,    // device interface name
                GENERIC_READ | GENERIC_WRITE,       // dwDesiredAccess
                FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
                NULL,                               // lpSecurityAttributes
                OPEN_EXISTING,                      // dwCreationDistribution
                0,                                  // dwFlagsAndAttributes
                NULL                                // hTemplateFile
                );

        if (interfaceDetailData)
            LocalFree(interfaceDetailData);

        if (hDevice == INVALID_HANDLE_VALUE)
        {
            wprintf(L"CreateFile failed with error: %d\n", GetLastError());
            return TRUE;
        }

        STORAGE_PROPERTY_QUERY                query;
        UCHAR                                outBuf[512];
        DWORD                                returnedLength;

       query.PropertyId = StorageDeviceProperty;
       query.QueryType = PropertyStandardQuery;
      
       status = DeviceIoControl( hDevice,               
                        IOCTL_STORAGE_QUERY_PROPERTY,
                        &query, sizeof( STORAGE_PROPERTY_QUERY ),
                        &outBuf, 512, &returnedLength, NULL
                        );

        if ( !status )
        {
            wprintf(L"IOCTL failed with error code: %d.\n\n", GetLastError() );
        }
        else
        {
            printStorageDeviceProperty(outBuf, returnedLength);
            printMediaType(hDevice);

            wprintf(L"\n\n");
        }

        if ( !CloseHandle(hDevice) )    
        {
            wprintf( L"Failed to close device.\n");
        }
    }    // End of for loop...

    if ( GetLastError() != NO_ERROR && GetLastError() != ERROR_NO_MORE_ITEMS )
    {
       return 1;
    }

    //  Cleanup
    SetupDiDestroyDeviceInfoList(hDevInfo);

    return 0;
}


The output looks like below:




Friday, March 8, 2013

Physical Disk (HDD) Adapter Information.

In my previous post, I tried build a command line app to show Physical Hard Drive and volume(s) mapping for each HDD present/attached to a system. Now I've extended the program to read and display properties of storage adapter for each physical hard drive. To achieve this I've used the very popular Win32 API, CreateFile() and IOCTL_STORAGE_QUERY_PROPERTY control code.

The previous program has been extended and now I'm showing only the part I've added on top of my previous program released under the title "PhysicalDisk and Volume Mapping information".

Here goes the rest of the code to get storage adapter's infotmation:

void PrintBusTypeName(BYTE iBusType)
{
    switch(iBusType)
    {
    case BusTypeUnknown:
        wprintf(L"BusType: Unknown\n");
        break;
    case BusTypeScsi:
        wprintf(L"BusType: SCSI\n");
        break;
    case BusTypeAtapi:
        wprintf(L"BusType: ATAPI\n");
        break;
    case BusTypeAta:
        wprintf(L"BusType: ATA\n");
        break;
    case BusType1394:
        wprintf(L"BusType: 1394\n");
        break;
    case BusTypeSsa:
        wprintf(L"BusType: SSA\n");
        break;
    case BusTypeFibre:
        wprintf(L"BusType: Fibre\n");
        break;
    case BusTypeUsb:
        wprintf(L"BusType: USB\n");
        break;
    case BusTypeRAID:
        wprintf(L"BusType: RAID\n");
        break;
    case BusTypeiScsi:
        wprintf(L"BusType: iSCSI\n");
        break;
    case BusTypeSas:
        wprintf(L"BusType: SAS\n");
        break;
    case BusTypeSata:
        wprintf(L"BusType: SATA\n");
        break;
    case BusTypeSd:
        wprintf(L"BusType: SD\n");
        break;
    case BusTypeMmc:
        wprintf(L"BusType: MMC\n");
        break;
    case BusTypeVirtual:
        wprintf(L"BusType: Virtual\n");
        break;
    case BusTypeFileBackedVirtual:
        wprintf(L"BusType: FileBackedVirtual\n");
        break;
    case BusTypeMax:
        wprintf(L"BusType: Max\n");
        break;
    case BusTypeMaxReserved:
        wprintf(L"BusType: Max Reserved\n");
        break;
    default:
        break;
    }
}

void printAdapterPropertiesHDD(DWORD iDrvNumber)
{
    TCHAR hddNum[5] = {0};
   
    swprintf_s(hddNum, 5, _T("%ld"), iDrvNumber);

    TCHAR szPhysicalDrv[STR_SIZE];
    memset(szPhysicalDrv, 0, STR_SIZE);

    HANDLE hDevice = INVALID_HANDLE_VALUE;
    _tcscpy_s(szPhysicalDrv, STR_SIZE, _T("\\\\.\\PhysicalDrive"));
    _tcscat_s(szPhysicalDrv, STR_SIZE, hddNum);

    hDevice = CreateFile(
                szPhysicalDrv,                        // device name
                GENERIC_READ | GENERIC_WRITE,       // dwDesiredAccess
                FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
                NULL,                               // lpSecurityAttributes
                OPEN_EXISTING,                      // dwCreationDistribution
                0,                                  // dwFlagsAndAttributes
                NULL                                // hTemplateFile
                );

    if(INVALID_HANDLE_VALUE == hDevice)
    {
        wprintf(L"CreateFile failed with error: %d\n", GetLastError());
        return;
    }
   
    STORAGE_PROPERTY_QUERY                query;
    PSTORAGE_ADAPTER_DESCRIPTOR            adpDesc;
    UCHAR                                outBuf[512];
    DWORD                                returnedLength;
    BOOL                                status;

    query.PropertyId = StorageAdapterProperty;
    query.QueryType = PropertyStandardQuery;
      
    status = DeviceIoControl(
        hDevice,
        IOCTL_STORAGE_QUERY_PROPERTY,
        &query,
        sizeof( STORAGE_PROPERTY_QUERY ),
        &outBuf,                  
        512,                     
        &returnedLength,     
        NULL                   
        );

    if ( !status )
    {
       wprintf(L"IOCTL failed with error code%d.\n\n", GetLastError() );
    }
    else
    {
        adpDesc = (PSTORAGE_ADAPTER_DESCRIPTOR) outBuf;
        wprintf( L"\nAdapter Properties\n");
        wprintf( L"------------------\n");
        PrintBusTypeName(adpDesc->BusType);
        wprintf( L"Max. Tr. Length: 0x%x\n", adpDesc->MaximumTransferLength );
        wprintf( L"Max. Phy. Pages: 0x%x\n", adpDesc->MaximumPhysicalPages );
       
        // Specifies the storage adapter's alignment requirements for transfers.
        // The alignment mask indicates alignment restrictions for buffers required by the storage adapter for transfer operations.
        switch(adpDesc->AlignmentMask)
        {
        case 0:
            wprintf(L"Storage adapter's alignment requirements for transfers: BYTE boundaries.\n");
            break;
        case 1:
            wprintf(L"Storage adapter's alignment requirements for transfers: WORD boundaries.\n");
            break;
        case 3:
            wprintf(L"Storage adapter's alignment requirements for transfers: DWORD32 boundaries.\n");
            break;
        case 7:
            wprintf(L"Storage adapter's alignment requirements for transfers: DWORD64 boundaries.\n");
            break;
        default:
            break;
        }

        // AdapterUsesPio
        if(adpDesc->AdapterUsesPio)
        {
            wprintf(L"The storage adapter uses programmed I/O (PIO).\n");
        }
        else
        {
            wprintf(L"The storage adapter doesn't use programmed I/O (PIO).\n");
        }

        // AdapterScansDown
        if(adpDesc->AdapterScansDown)
        {
            wprintf(L"The storage adapter begins scanning with the highest device number, ");
            wprintf(L"that is, the storage adapter scans down for BIOS devices.\n");
        }
        else
        {
            wprintf(L"The storage adapter begins scanning with the lowest device number.\n");
        }

        // AcceleratedTransfer
        if(adpDesc->AcceleratedTransfer)
        {
            wprintf(L"The storage adapter supports synchronous transfers ");
            wprintf(L"as a way of speeding up I/O.\n");
        }
        else
        {
            wprintf(L"The storage adapter does not support synchronous ");
            wprintf(L"transfers as a way of speeding up I/O.\n");
        }

        // CommandQueueing
        if(adpDesc->CommandQueueing)
        {
            wprintf(L"The storage adapter supports SCSI tagged queuing and");
            wprintf(L"/or per-logical-unit internal queues, or the non-SCSI equivalent.\n");
        }
        else
        {
            wprintf(L"The storage adapter neither supports SCSI-tagged queuing ");
            wprintf(L"nor per-logical-unit internal queues.\n");
        }

        // BusMajorVersion
        wprintf(L"The storage adapter major Version: %d\n", adpDesc->BusMajorVersion);

        // BusMinorVersion
        wprintf(L"The storage adapter minor Version: %d\n", adpDesc->BusMinorVersion);
    }

    if( !CloseHandle(hDevice) )
    {
        wprintf(L"Failed to close drive %s.\n\n", szPhysicalDrv);
    }

    wprintf(L"\n");
}

In the _tmain....., called printAdapterPropertiesHDD from following location.

:::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::

for( std::map < DWORD, DWORD >::iterator ii = mapPhysicaDriveCnt.begin(); ii != mapPhysicaDriveCnt.end(); ++ii)
        {
            wprintf(L"No. of volume(s) on physical drive %ld is/are: %ld\n", (*ii).first, mapDriveVolume.count((*ii).first));
           
            std::pair < std::multimap < DWORD, TCHAR * >::iterator, std::multimap < DWORD, TCHAR * >::iterator > ret;
            ret = mapDriveVolume.equal_range((*ii).first);
           
            for (std::multimap< DWORD, TCHAR * >::iterator it=ret.first; it!=ret.second; ++it)
            {
                wprintf(L"Volume on physical drive: %ld is: %s\n", it->first, it->second);
            }
            wprintf(L"\n");

            printAdapterPropertiesHDD((*ii).first);
        }

The output looks like below:


Thursday, March 7, 2013

PhysicalDisk and Volume Mapping information

After a long time, writing this post on Windows Disk Management. I was playing with volume management APIs and found it's quite easy to get some useful information like, how do I know what are the volume(s) present on a physical disk. I'm talking about hard drive here. Of course windows disk manager will reveal it but why not I should have my own app. There are Windows Volume Management APIs including very famous CreateFile API, which is one of amazing API on windows provided by Microsoft.

The objective was to build a tool which will tell How many HDDs are attached to the system with volume information. Here is the complete code:


const int BUFF_SIZE        = 512;
const int STR_SIZE        = 20;
const int DRIVE_ID_BUFF = 3;

int _tmain(int argc, _TCHAR* argv[])
{
    TCHAR szTemp[BUFF_SIZE];
   
    if (GetLogicalDriveStrings(BUFF_SIZE - 1, szTemp))
    {
        TCHAR szDrive[DRIVE_ID_BUFF] = TEXT(" :");
        TCHAR* pDrive = szTemp;

        std::multimap < DWORD, TCHAR * > mapDriveVolume;
        std::map < DWORD, DWORD > mapPhysicaDriveCnt;
        HANDLE   hLogicalDisk    =    INVALID_HANDLE_VALUE;
       
        while(*pDrive)
        {
            pDrive += _tcslen( pDrive ) + 1;

            *szDrive = *pDrive;

            TCHAR szLogicalDrive[STR_SIZE];
            memset(szLogicalDrive, 0, STR_SIZE);
            _tcscpy_s(szLogicalDrive, STR_SIZE, _T("\\\\.\\"));
            _tcscat_s(szLogicalDrive, STR_SIZE, szDrive);
           
            if(_tcscmp(szLogicalDrive, _T("\\\\.\\")) != 0)
            {
                BOOL bResult            =    FALSE; // results flag
                VOLUME_DISK_EXTENTS    vDiskExtent = {0};
                DWORD bytesReturned        = 0;
               
                hLogicalDisk = CreateFileW(szLogicalDrive,     // drive to open
                            0,            // no access to the drive
                            FILE_SHARE_READ |    // share mode
                            FILE_SHARE_WRITE,
                            NULL,            // default security attributes
                            OPEN_EXISTING,        // disposition
                            0,                // file attributes
                            NULL);

                if(INVALID_HANDLE_VALUE == hLogicalDisk)
                {
                    wprintf(L"Error in CreateFile() API with error: %ld\n", GetLastError());
                }
                else
                {
                    bResult = bResult = DeviceIoControl(hLogicalDisk,            // device to be queried
                                IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,        // operation to perform
                                NULL, 0,                    // no input buffer
                                &vDiskExtent, sizeof(VOLUME_DISK_EXTENTS),    // output buffer
                                &bytesReturned,                    // # bytes returned
                                (LPOVERLAPPED) NULL);                // synchronous I/O
                }

                LARGE_INTEGER extLength = vDiskExtent.Extents->ExtentLength;
                if(extLength.QuadPart > 0)
                {
                    // This is a valid volume
                   
                    mapDriveVolume.insert(std::pair < DWORD, TCHAR * >(vDiskExtent.Extents->DiskNumber, pDrive));
                    mapPhysicaDriveCnt.insert(std::pair < DWORD, DWORD >(vDiskExtent.Extents->DiskNumber, vDiskExtent.Extents->DiskNumber));
                }
            }

            CloseHandle(hLogicalDisk);
        }    // End of while

        for( std::map < DWORD, DWORD >::iterator ii = mapPhysicaDriveCnt.begin(); ii != mapPhysicaDriveCnt.end(); ++ii)
        {
            wprintf(L"Number of volume in physical drive %ld is %ld\n", (*ii).first, mapDriveVolume.count((*ii).first));
           
            std::pair < std::multimap >DWORD, TCHAR * >::iterator, std::multimap>DWORD, TCHAR * >::iterator > ret;
            ret = mapDriveVolume.equal_range((*ii).first);
           
            for (std::multimap < DWORD, TCHAR * >::iterator it=ret.first; it!=ret.second; ++it)
            {
                wprintf(L"Volumes in physical drive: %ld is/are %s\n", it->first, it->second);
            }
            wprintf(L"\n");
        }
    }

    return 0;
}

Output will be like below: