Advertisement
Guest User

libvirt probe host OS

a guest
Jun 11th, 2015
280
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 20.05 KB | None | 0 0
  1. /*  "probeOs.c"
  2.  
  3. Summary:
  4.     Experimental tool built on top of 'libvirt' for analyzing
  5.     guest OS kernels via introspection (memory snooping).
  6.  
  7. Copy:
  8.     Copyright (C) 2012, Dennis Jenkins.
  9.  
  10. License:
  11.     Gnu Public License, Version 2.1, 1999.
  12.  
  13. Author(s):
  14.     Dennis Jenkins <dennis.jenkins.75@gmail.com>
  15.  
  16.  
  17. Notes:
  18.     Immense thanks to Harlan Carvey (keydet89@yahoo.com), for his original
  19.     perl utility, "kern.pl" (v.0.1_20060914).  Harlan granted permission
  20.     to republish his NT kernel offset table.  Thanks Harlan!
  21.  
  22.     2012-04-17: Can detect Win32 NT4, W2K, XP, W2K3
  23.  
  24.     Linux kernels (support not in yet) are easy to identify, but require
  25.     _LOTS_ of memory probes, and those are expesive via libvirt.
  26.  
  27.     Win9x kernls are nasty beasts.  I must write an MZ/NE parser, and win16
  28.     resource reader to locate "KRNL386.EXE".
  29.  
  30.     Solaris 11 11/11 leaves tell-tale debries in the first 1M of ram.
  31.     Grub loads the kernel image "unix" at the 1M mark, but
  32.     the kernel relocates itself (don't know where yet), so positive
  33.     identification eludes me.
  34.  
  35.     FreeBSD has not been investigated yet.
  36.  
  37. References:
  38.     https://en.wikibooks.org/wiki/X86_Disassembly/Windows_Executable_Files#MS-DOS_header
  39.     https://www.blackhat.com/presentations/bh-usa-06/BH-US-06-Burdach.pdf
  40. */
  41.  
  42. // gcc -std=c99 -Wall -O2 -pipe misc/probeOs.c -o tmp/probeOs -lvirt
  43.  
  44. #include <assert.h>
  45. #include <stddef.h>
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <string.h>
  49. #include <libvirt/libvirt.h>
  50.  
  51. static const char *szUri = "qemu+tls://localhost/system";
  52.  
  53. static const int DEBUG = 0;
  54.  
  55. #define CP() do { if (DEBUG) printf ("checkpoint: %d\n", __LINE__); } while (0)
  56.  
  57. static unsigned int MAX_MEM_RANGE = 65536;
  58. static unsigned short RT_VERSION = 0x10;
  59.  
  60. /* Table of physical addresses to check for the MZ/PE header for "NTOSKRNL.EXE". */
  61. /* Shamelessly taken from Harlan Carvey. */
  62. static unsigned long win32_krnl_offsets[] =
  63. {
  64.     0x00100000,     // NT4
  65.     0x00400000,     // 2000
  66.     0x004d4000,     // XP
  67.     0x004d0000,     // XP
  68.     0x004d5000,     // XP
  69.     0x00a02000,     // XP
  70.     0x004d7000,     // XP-SP2
  71.     0x004de000,     // 2003
  72.     0x00800000,     // 2003-SP1
  73.     (unsigned long)-1,  // end of list.
  74. };
  75.  
  76. struct COFFHeader
  77. {
  78.     unsigned short int  Machine;        // 2 bytes
  79.     unsigned short int  NumberOfSections;   // 2 bytes
  80.     unsigned int        TimeDateStamp;      // 4 bytes
  81.     unsigned int        PointerToSymbolTable;   // 4 bytes
  82.     unsigned int        NumberOfSymbols;    // 4 bytes
  83.     unsigned short int  SizeOfOptionalHeader;   // 2 bytes
  84.     unsigned short int  Characteristics;    // 2 bytes
  85. };
  86.  
  87. struct data_directory
  88. {
  89.     unsigned int    VirtualAddress;
  90.     unsigned int    Size;
  91. };
  92.  
  93. struct PEOptHeader
  94. {
  95.     unsigned short  signature;      //decimal number 267.
  96.     unsigned char   MajorLinkerVersion;
  97.     unsigned char   MinorLinkerVersion;
  98.     unsigned int    SizeOfCode;
  99.     unsigned int    SizeOfInitializedData;
  100.     unsigned int    SizeOfUninitializedData;
  101.     unsigned int    AddressOfEntryPoint;    // The RVA of the code entry point
  102.     unsigned int    BaseOfCode;
  103.     unsigned int    BaseOfData;
  104.     unsigned int    ImageBase;
  105.     unsigned int    SectionAlignment;
  106.     unsigned int    FileAlignment;
  107.     unsigned short  MajorOSVersion;
  108.     unsigned short  MinorOSVersion;
  109.     unsigned short  MajorImageVersion;
  110.     unsigned short  MinorImageVersion;
  111.     unsigned short  MajorSubsystemVersion;
  112.     unsigned short  MinorSubsystemVersion;
  113.     unsigned int    Reserved;
  114.     unsigned int    SizeOfImage;
  115.     unsigned int    SizeOfHeaders;
  116.     unsigned int    Checksum;
  117.     unsigned short  Subsystem;
  118.     unsigned short  DLLCharacteristics;
  119.     unsigned int    SizeOfStackReserve;
  120.     unsigned int    SizeOfStackCommit;
  121.     unsigned int    SizeOfHeapReserve;
  122.     unsigned int    SizeOfHeapCommit;
  123.     unsigned int    LoaderFlags;
  124.     unsigned int    NumberOfRvaAndSizes;
  125.     struct data_directory DataDirectory[16];     // Can have any number of elements, matching the number in NumberOfRvaAndSizes.
  126. };
  127.  
  128. static const int IMAGE_DIRECTORY_ENTRY_EXPORT = 0;      // Location of the export directory
  129. static const int IMAGE_DIRECTORY_ENTRY_IMPORT = 1;      // Location of the import directory
  130. static const int IMAGE_DIRECTORY_ENTRY_RESOURCE = 2;        // Location of the resource directory
  131. static const int IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11;   // Location of alternate import-binding directory
  132.  
  133. struct  ImageSectionHeader      // 40 bytes
  134. {
  135.     char        sName[8];   // 8 bytes, NOT guaranteed to be terminated.
  136.     unsigned int    nVirtSize;  // 4 bytes, size once loaded into memory.
  137.     unsigned int    nVirtAddr;  // 4 bytes, location once loaded into memory.
  138.     unsigned int    nPhysSize;  // 4 bytes, size on disk.
  139.     unsigned int    nPhysAddr;  // 4 bytes, offset in file on disk.
  140.     unsigned char   zReserved[12];  // 12 bytes, usually zero.
  141.     unsigned int    nSectionFlags;  // 4 bytes
  142. };
  143.  
  144. // Win32 reource directory table headr (16 bytes)
  145. // Named "IMAGE_RESOURCE_DIRECTORY" in the msdn (I abbreviated)
  146. struct  IMG_RES_DIR_HDR
  147. {
  148.     unsigned int    cCharacteristics;   // 4 bytes, flags
  149.     unsigned int    nDateTimeStamp;     // 4 bytes, unix time stamp
  150.     unsigned short  nMajorVersion;      // 2 bytes
  151.     unsigned short  nMinorVersion;      // 2 bytes
  152.     unsigned short  nCountNames;        // 2 bytes, count of "name" entries.
  153.     unsigned short  nCountIDs;      // 2 bytes, count of "id" entries.
  154. };
  155.  
  156. // Named "IMAGE_RESOURCE_DIRECTORY_ENTRY"
  157. struct  IMG_RES_DIR_ENTRY
  158. {
  159.     unsigned int        nName;
  160.     unsigned int        nDataOffset;
  161. };
  162.  
  163. struct  IMG_RES_DIR
  164. {
  165.     struct IMG_RES_DIR_HDR      header;
  166.     struct IMG_RES_DIR_ENTRY    entry[1];
  167. };
  168.  
  169. struct  RES_DATA_ENTRY
  170. {
  171.     unsigned int        nDataRva;   // 4 bytes
  172.     unsigned int        nSize;      // 4 bytes
  173.     unsigned int        nCodePage;  // 4 bytes
  174.     unsigned int        nReserved;  // 4 bytes
  175. };
  176.  
  177. struct  FIXED_FILE_INFO     // "VS_FIXEDFILEINFO", count 13, 4-byte dwords.
  178. {
  179.     unsigned int        dwSignature;
  180.     unsigned int        dwStructVersion;
  181.     unsigned int        dwFileVersionMS;
  182.     unsigned int        dwFileVersionLS;
  183.     unsigned int        dwProductVersionMS;
  184.     unsigned int        dwProductVersionLS;
  185.     unsigned int        dwFileFlagsMask;
  186.     unsigned int        dwFileFlags;
  187.     unsigned int        dwFileOS;
  188.     unsigned int        dwFileType;
  189.     unsigned int        dwFileSubtype;
  190.     unsigned int        dwFileDateMS;
  191.     unsigned int        dwFileDateLS;
  192. };
  193.  
  194. // Stuff from the 'VS_VERSIONINFO'
  195.  
  196. struct  WindowsVersionInfo
  197. {
  198.     struct FIXED_FILE_INFO  ffi;
  199.  
  200. // The following are NOT loaded from the unicode strings yet (I'm too lazy and don't need them).
  201. //  char    *szFileDescription;
  202. //  char    *szFileVersion;
  203. //  char    *szInternalName;
  204. //  char    *szOriginalFileName;
  205. //  char    *szProductName;
  206. //  char    *szProductVersion;
  207. };
  208.  
  209. // This struct represents what we are trying to find in the domain's RAM:
  210. // The win32 file info for the OS.
  211. struct Win32Kernel
  212. {
  213.     unsigned long           nBaseAddr;  // Where the kernel is loaded ("MZ" header)
  214.     struct WindowsVersionInfo   vi;
  215. };
  216.  
  217. struct DomainKernelInfo
  218. {
  219.     unsigned int            nKernelType;    // 0 = Microsoft
  220.     union {
  221.         struct Win32Kernel  win32;
  222.     }               info;
  223. };
  224.  
  225.  
  226.  
  227. // Like a "VFS" for accessing memory from a domain.
  228. struct      domWrapper
  229. {
  230.     virDomainPtr    pDom;
  231.     unsigned long   nKB;
  232.     unsigned long long nBaseAddr;
  233.     unsigned char   *pBuffer;
  234.     unsigned long   nBufSize;
  235.     int     nFlags;     // DMW_xxxxx
  236. };
  237.  
  238. static const unsigned int DMW_VALID = 0x0001;
  239.  
  240. static struct domWrapper*   domWrapperAlloc (virDomainPtr pDom)
  241. {
  242.     struct domWrapper *p = NULL;
  243.  
  244.     if (NULL == pDom)
  245.     {
  246.         return NULL;
  247.     }
  248.  
  249.     if (NULL == (p = (struct domWrapper*)malloc (sizeof (*p))))
  250.     {
  251.         return NULL;
  252.     }
  253.  
  254.     memset (p, 0, sizeof(*p));
  255.  
  256.     p->pDom = pDom;
  257.     p->nKB = virDomainGetMaxMemory (pDom);
  258.     p->nBaseAddr = 0;
  259.     p->nBufSize = 65536;
  260.     if (NULL == (p->pBuffer = (unsigned char*)malloc (p->nBufSize)))
  261.     {
  262.         free (p);
  263.         return NULL;
  264.     }
  265.     memset (p->pBuffer, 0, p->nBufSize);
  266.     p->nFlags = 0;
  267.  
  268.     return p;
  269. }
  270.  
  271. static void domWrapperDestroy (struct domWrapper *pDMW)
  272. {
  273.     if (pDMW)
  274.     {
  275.         if (pDMW->pBuffer)
  276.         {
  277.             free (pDMW->pBuffer);
  278.         }
  279.  
  280.         free (pDMW);
  281.     }
  282. }
  283.  
  284. // Copies 'nBytes' bytes from nAbsAddr in the domain (physical memory) into 'pBuffer'.
  285. // Returns '1' on success, '0' on failure.
  286. // We expect many copy operations to come from the same "block", so we cache a 64K chunk
  287. // of the domain's memory.
  288. static int  domWrapperCopy (struct domWrapper *pDomWrapper, unsigned long int nAbsAddr, unsigned int nBytes, void *pBuffer)
  289. {
  290.     if (!pDomWrapper || !nBytes || !pBuffer || (nBytes > MAX_MEM_RANGE) || (nBytes > pDomWrapper->nBufSize))
  291.     {
  292.         return 0;
  293.     }
  294.  
  295.     if (!(pDomWrapper->nFlags & DMW_VALID) ||
  296.         (nAbsAddr < pDomWrapper->nBaseAddr) ||
  297.         ((nAbsAddr + nBytes) >= (pDomWrapper->nBaseAddr + pDomWrapper->nBufSize)))
  298.     {
  299.         if (DEBUG)
  300.         {
  301.             printf ("INFO: virDomainMemoryPeek (%08lx, %lu)\n", nAbsAddr, pDomWrapper->nBufSize);
  302.         }
  303.  
  304.         if (0 != virDomainMemoryPeek (pDomWrapper->pDom, nAbsAddr, pDomWrapper->nBufSize, pDomWrapper->pBuffer, VIR_MEMORY_PHYSICAL))
  305.         {
  306.             pDomWrapper->nFlags &= ~DMW_VALID;
  307.             return 0;
  308.         }
  309.  
  310.         pDomWrapper->nFlags |= DMW_VALID;
  311.         pDomWrapper->nBaseAddr = nAbsAddr;
  312.     }
  313.  
  314.     memcpy (pBuffer, pDomWrapper->pBuffer + (nAbsAddr - pDomWrapper->nBaseAddr), nBytes);
  315.     return 1;
  316. }
  317.  
  318. /* Quick little tool to aid in development + debugging. */
  319. static void hexDump (const void *buf, unsigned int count, unsigned long int baseAddr)
  320. {
  321.     const unsigned char *p = (const unsigned char *)buf;
  322. //  unsigned long int newBase = baseAddr & (unsigned long int)(~0xf);
  323.  
  324. //  printf ("ba = %lu, nb = %lu\n", baseAddr, newBase);
  325.  
  326.     while (count)
  327.     {
  328.         printf (" %02x", *p);
  329.         p++;
  330.         count--;
  331.     }
  332.     printf ("\n");
  333. }
  334.  
  335. static void dumpCoffHeader (const struct COFFHeader *pCoff)
  336. {
  337.     printf ("\tCOFF: "); hexDump (pCoff, sizeof(*pCoff), 0);
  338.     printf ("\tcoff.Machine = %04x\n", pCoff->Machine);
  339.     printf ("\tcoff.NumberOfSections = %04x\n", pCoff->NumberOfSections);
  340.     printf ("\tcoff.TimeDateStamp = %08x\n", pCoff->TimeDateStamp);
  341.     printf ("\tcoff.PointerToSymbolTable = %08x\n", pCoff->PointerToSymbolTable);
  342.     printf ("\tcoff.NumberOfSymbols = %04x\n", pCoff->NumberOfSymbols);
  343.     printf ("\tcoff.SizeOfOptionalHeader = %04x\n", pCoff->SizeOfOptionalHeader);
  344.     printf ("\tcoff.Characteristics = %04x\n", pCoff->Characteristics);
  345. }
  346.  
  347. static void dumpPEHeader (const struct PEOptHeader *peOpt)
  348. {
  349.     printf ("\tPE.SubSystem = %d\n", peOpt->Subsystem);
  350.     printf ("\tPE.rva_num = %d\n", peOpt->NumberOfRvaAndSizes);
  351.  
  352. // Loop through the RVA sections.
  353.     for (int i = 0; i < peOpt->NumberOfRvaAndSizes; i++)
  354.     {
  355.         if (!peOpt->DataDirectory[i].VirtualAddress && !peOpt->DataDirectory[i].Size)
  356.         {
  357.             continue;
  358.         }
  359.  
  360.         printf ("\tpe.rva[%2d] = %08x, %08x\n", i, peOpt->DataDirectory[i].VirtualAddress, peOpt->DataDirectory[i].Size);
  361.     }
  362. }
  363.  
  364. static struct IMG_RES_DIR*  loadImgResDir (struct domWrapper *pDomWrapper, unsigned long baseAddr, unsigned long nImgResDirPtr)
  365. {
  366.     struct IMG_RES_DIR  *pDir = NULL;
  367.     struct IMG_RES_DIR_HDR  hdr = {0};
  368.     unsigned int        nBytes = 0;
  369.  
  370.     if (!pDomWrapper || !baseAddr || !nImgResDirPtr)
  371.     {
  372.         CP(); return NULL;
  373.     }
  374.  
  375. // Step #1, read the header.
  376.     if (!domWrapperCopy (pDomWrapper, baseAddr + nImgResDirPtr, sizeof (hdr), &hdr))
  377.     {
  378.         CP(); return NULL;
  379.     }
  380.  
  381. // Step #2, allocate full buffer.
  382.     nBytes = sizeof(hdr) + sizeof(struct IMG_RES_DIR_ENTRY) * (hdr.nCountNames + hdr.nCountIDs);
  383.     if (NULL == (pDir = (struct IMG_RES_DIR*)malloc (nBytes)))
  384.     {
  385.         CP(); return NULL;
  386.     }
  387.  
  388. // Step #3, read entire IMG_RES_DIR.
  389.     if (!domWrapperCopy (pDomWrapper, baseAddr + nImgResDirPtr, nBytes, pDir))
  390.     {
  391.         free (pDir);
  392.         CP(); return NULL;
  393.     }
  394.  
  395. // Debugging
  396. #if 0
  397.     printf ("RES [%08lx] = ", nImgResDirPtr);
  398.     hexDump (pDir, 16, 0);
  399.     for (unsigned int i = 0; i < (hdr.nCountNames + hdr.nCountIDs); i++)
  400.     {
  401.         printf ("\tver[%2d] = %08x, %08x\n", i, pDir->entry[i].nName, pDir->entry[i].nDataOffset);
  402.     }
  403. #endif
  404.  
  405.     return pDir;
  406. }
  407.  
  408. // http://msdn.microsoft.com/en-us/library/ms809762.aspx
  409. // ~80% down the page, under "PE File Resources"
  410. static struct WindowsVersionInfo*   findVersionInfo (struct domWrapper *pDomWrapper, unsigned long baseAddr, unsigned int nImgResDirPtr)
  411. {
  412.     struct WindowsVersionInfo *pVer = NULL;
  413.     struct IMG_RES_DIR *pTypes = NULL;  // level-0
  414.     struct IMG_RES_DIR *pNames = NULL;  // level-1
  415.     struct IMG_RES_DIR *pLangs = NULL;  // level-2
  416.     struct RES_DATA_ENTRY dataEntry = {0};
  417.     struct FIXED_FILE_INFO ffi = {0};
  418.     unsigned int i = 0;
  419.     unsigned int nCount = 0;
  420.     unsigned int nOffset = 0;
  421.  
  422.     if (!pDomWrapper || !baseAddr || !nImgResDirPtr)
  423.     {
  424.         CP(); goto done;
  425.     }
  426.  
  427. // The ".rscs" is a tree structure.  Most actual resource content is three layers down.
  428.  
  429. // Step #1, load the root entry.  It contains a table of each resource type
  430.     if (NULL == (pTypes = loadImgResDir (pDomWrapper, baseAddr, nImgResDirPtr)))
  431.     {
  432.         CP(); goto done;
  433.     }
  434.  
  435. // Step #2, locate the "RT_VERSION" resource directory entry.
  436.     nCount = pTypes->header.nCountNames + pTypes->header.nCountIDs;
  437.     if (!nCount) { CP(); goto done; }
  438.     for (i = 0; (i < nCount) && (pTypes->entry[i].nName != RT_VERSION); i++);
  439.  
  440. // Step 2.5, sanity checking.
  441.     if (i >= nCount) { CP(); goto done; }
  442.     if (!(pTypes->entry[i].nDataOffset & 0x80000000)) { CP(); goto done; }
  443.  
  444. // Step #3, load the table of names for the selected type.
  445. // There should be one entry each each resource of this type.
  446. // Generally "RT_VERSION" should have ony one entry.
  447.     nOffset = nImgResDirPtr + (pTypes->entry[i].nDataOffset & 0x7fffffff);
  448.     if (NULL == (pNames = loadImgResDir (pDomWrapper, baseAddr, nOffset )))
  449.     {
  450.         CP(); goto done;
  451.     }
  452.  
  453. // Step #3.5, sanity checking on language table.
  454.     nCount = pNames->header.nCountNames + pNames->header.nCountIDs;
  455.     if (!nCount) { CP(); goto done; }
  456.     if (!(pNames->entry[0].nDataOffset & 0x80000000)) { CP(); goto done; }
  457.  
  458. // Step #4, Ignore the name, just take the first (and usually only) entry.
  459. // This is the table of languages.  RT_VERSION typically has only one entry.
  460.     nOffset = nImgResDirPtr + (pNames->entry[0].nDataOffset & 0x7fffffff);
  461.     if (NULL == (pLangs = loadImgResDir (pDomWrapper, baseAddr, nOffset )))
  462.     {
  463.         CP(); goto done;
  464.     }
  465.  
  466.     nCount = pLangs->header.nCountNames + pLangs->header.nCountIDs;
  467.     if (!nCount) { CP(); goto done; }
  468.  
  469. // Should be a "leaf" node.
  470.     if (pLangs->entry[0].nDataOffset & 0x80000000) { CP(); goto done; }
  471.  
  472. // Load the leaf node (8 bytes)
  473.     nOffset = nImgResDirPtr + (pLangs->entry[0].nDataOffset & 0x7fffffff);
  474.     if (!domWrapperCopy (pDomWrapper, baseAddr + nOffset, sizeof(dataEntry), &dataEntry))
  475.     {
  476.         CP(); goto done;
  477.     }
  478.  
  479. // Step #4.5, sanity checking.
  480.     if (dataEntry.nSize > 16384) { CP(); goto done; }   // random guess at reasonable limit.
  481.  
  482. // Step #5, load the "VS_FIXEDFILEINFO" part of the resource.
  483.     nOffset = dataEntry.nDataRva + 6 + 0x22;
  484.     if (!domWrapperCopy (pDomWrapper, baseAddr + nOffset, sizeof(ffi), &ffi))
  485.     {
  486.         CP(); goto done;
  487.     }
  488.  
  489.     if (ffi.dwSignature != 0xFEEF04BD)
  490.     {
  491.         CP(); goto done;
  492.     }
  493.  
  494.     if (NULL == (pVer = malloc (sizeof(*pVer))))
  495.     {
  496.         CP(); goto done;
  497.     }
  498.  
  499.     pVer->ffi = ffi;
  500.  
  501. done:
  502.     if (pTypes) free (pTypes);
  503.     if (pNames) free (pNames);
  504.     if (pLangs) free (pLangs);
  505.  
  506.     return pVer;
  507. }
  508.  
  509. // Scan memory of domain, looking for a matching win32 kernel image ("ntoskrnl.exe")
  510. static struct WindowsVersionInfo*   probeWin32Kernel (struct domWrapper *pDomWrapper, unsigned long baseAddr)
  511. {
  512.     unsigned long       e_lfanew = 0;
  513.     unsigned int        rvaPEOptionHeader = 0;
  514.     unsigned int        rvaSectionHeaders = 0;
  515.     unsigned int        i = 0;
  516.     unsigned char       mz_hdr[4096];
  517.     struct COFFHeader   coff = {0};
  518.     struct PEOptHeader  peOpt = {0};
  519.     struct ImageSectionHeader rscsHdr = {"", 0};
  520.     unsigned char       *pBuffer = mz_hdr + 0;  // Alias, for brevity.
  521.     struct WindowsVersionInfo *pVer = NULL;
  522.  
  523. // FIXME: big-ass-assumption: the MZ header, PE header both fit within the first 4K of the image.
  524.     if (!domWrapperCopy (pDomWrapper, baseAddr, sizeof (mz_hdr), mz_hdr))
  525.     {
  526.         CP(); return NULL;
  527.     }
  528.  
  529. // Check for "MZ" header.
  530.     if (0x5a4d != *(unsigned short int*)(pBuffer + 0))
  531.     {
  532.         CP(); goto fail;
  533.     }
  534.  
  535. // Get offset from MZ header to the PE header (aka, "e_lfanew" value).
  536.     e_lfanew = *(unsigned int*)(pBuffer + 0x3c);
  537.     if (e_lfanew > (sizeof(mz_hdr) - 4))
  538.     {
  539.         CP(); goto fail;
  540.     }
  541.  
  542. // The PE header should be 8-byte aligned.
  543.     if (e_lfanew & 0x7)
  544.     {
  545.         CP(); goto fail;
  546.     }
  547.  
  548. // Check for PE header.
  549.     if (0x00004550 != *(unsigned int*)(pBuffer + e_lfanew))
  550.     {
  551.         CP(); goto fail;
  552.     }
  553.  
  554. // COFF header immediately follows PE header.
  555.     if (!domWrapperCopy (pDomWrapper, baseAddr + e_lfanew + 4, sizeof(coff), &coff))
  556.     {
  557.         CP(); goto fail;
  558.     }
  559.  
  560. // Sanity check the COFF header.
  561.     if (coff.Machine != 0x14C)  // magic code for "i386"
  562.     {
  563.         CP(); goto fail;
  564.     }
  565.  
  566. // According to wikibooks, all PE headers are a fixed size (even though thry could be variable).
  567.     if (coff.SizeOfOptionalHeader != sizeof (struct PEOptHeader))
  568.     {
  569.         CP(); goto fail;
  570.     }
  571.  
  572. //  dumpCoffHeader (&coff);
  573.  
  574. // Grab the PE "optional" (not really) header.
  575.     rvaPEOptionHeader = e_lfanew + 24;
  576.     if (!domWrapperCopy (pDomWrapper, baseAddr + rvaPEOptionHeader, coff.SizeOfOptionalHeader, &peOpt))
  577.     {
  578.         CP(); goto fail;
  579.     }
  580.  
  581. // Yeah for magic numbers.  (See wikibooks article).
  582.     if (peOpt.signature != 267)
  583.     {
  584.         CP(); goto fail;
  585.     }
  586.  
  587. //  dumpPEHeader (&peOpt);
  588. //  printf ("\tMZ header @ %08lx\n", baseAddr);
  589. //  printf ("\tPE.DataDirectory @ %08lx\n", baseAddr + e_lfanew + 24 + offsetof (struct PEOptHeader, DataDirectory));
  590.  
  591.     struct data_directory   ddRes = {0};        // for brevity
  592.     memcpy (&ddRes, peOpt.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE, sizeof(ddRes));
  593.     if (!ddRes.VirtualAddress || !ddRes.Size)
  594.     {
  595.         CP(); goto fail;
  596.     }
  597.  
  598. // The "Section Headers" immediately follow the DataDirectory.
  599. // Source: Page 23, Revision 8.2 (2010-09-21), "pecoff_v8", Microsoft Inc.
  600.  
  601. // Loop through each section, looking for ".rscs" (resources)
  602.     rvaSectionHeaders = rvaPEOptionHeader + coff.SizeOfOptionalHeader;
  603.     for (i = 0; i < coff.NumberOfSections; i++)
  604.     {
  605.         unsigned int offRscs = i * sizeof (struct ImageSectionHeader);
  606.  
  607.         if (!domWrapperCopy (pDomWrapper, baseAddr + rvaSectionHeaders + offRscs, sizeof (rscsHdr), &rscsHdr))
  608.         {
  609.             CP(); goto fail;
  610.         }
  611.  
  612.         if (!memcmp (rscsHdr.sName, ".rsrc\0\0\0", 8))
  613.         {
  614.             break;
  615.         }
  616.     }
  617.  
  618.     if (i >= coff.NumberOfSections)
  619.     {
  620.         CP(); goto fail;
  621.     }
  622.  
  623. // Sanity check.
  624.     if (rscsHdr.nVirtAddr != ddRes.VirtualAddress)
  625.     {
  626.         CP(); goto fail;
  627.     }
  628.  
  629.     if (NULL == (pVer = findVersionInfo (pDomWrapper, baseAddr, rscsHdr.nVirtAddr)))
  630.     {
  631.         CP(); goto fail;
  632.     }
  633.  
  634.     return pVer;
  635.  
  636. fail:
  637.     return NULL;
  638. }
  639.  
  640. // Given a domain, attempt to determine its operatin system.
  641. static void processDomain (virDomainPtr pDom)
  642. {
  643.     const char *name = NULL;
  644.     unsigned long kb = 0;   // RAM, in kilobytes.
  645.     int     i = 0;
  646.     struct domWrapper   *pDomWrapper = NULL;
  647.  
  648.     if (!pDom) return;
  649.     name = virDomainGetName (pDom);
  650.     if (!name) return;
  651.  
  652. //  printf ("Probing: %s\n", name);
  653.  
  654.     kb = virDomainGetMaxMemory (pDom);
  655. //  printf ("\tRAM = %lu KiB\n", kb);
  656.  
  657.     if (NULL == (pDomWrapper = domWrapperAlloc (pDom)))
  658.     {
  659.         CP(); return;
  660.     }
  661.  
  662.     for (i = 0; win32_krnl_offsets[i] != (unsigned long)-1; i++)
  663.     {
  664.         struct WindowsVersionInfo *pVer = probeWin32Kernel (pDomWrapper, win32_krnl_offsets[i]);
  665.         if (!pVer) continue;
  666.  
  667.         char ver[64];
  668.         snprintf (ver, sizeof(ver), "%d.%d.%d.%d",
  669.             pVer->ffi.dwProductVersionMS >> 16,
  670.             pVer->ffi.dwProductVersionMS & 0xffff,
  671.             pVer->ffi.dwProductVersionLS >> 16,
  672.             pVer->ffi.dwProductVersionLS & 0xffff);
  673.  
  674.         printf ("%-20.20s  %6luKiB  [%08lx] %s\n", name, kb, win32_krnl_offsets[i], ver);
  675.         break;
  676.     }
  677.  
  678.     domWrapperDestroy (pDomWrapper);
  679. }
  680.  
  681. int main (int argc, char *argv[])
  682. {
  683.     virConnectPtr   pVmm = NULL;
  684.     virDomainPtr    pDom = NULL;
  685.     int     numDomains = 0;
  686.     int     i;
  687.     int     *domainIds = NULL;
  688.  
  689.     assert (sizeof(struct COFFHeader) == 20);
  690.     assert (sizeof(struct ImageSectionHeader) == 40);
  691.     assert (sizeof(struct IMG_RES_DIR_HDR) == 16);
  692.     assert (sizeof(struct IMG_RES_DIR_ENTRY) == 8);
  693.     assert (sizeof(struct RES_DATA_ENTRY) == 16);
  694.  
  695.     if (NULL == (pVmm = virConnectOpen (szUri)))
  696.     {
  697.         fprintf (stderr, "Failed to connect: %s\n", szUri);
  698.         exit (-1);
  699.     }
  700.  
  701.     if (-1 == (numDomains = virConnectNumOfDomains(pVmm)))
  702.     {
  703.         goto done;
  704.     }
  705.  
  706.     if (NULL == (domainIds = malloc(sizeof(int) * numDomains)))
  707.     {
  708.         goto done;
  709.     }
  710.  
  711.     if (-1 == (numDomains = virConnectListDomains (pVmm, domainIds, numDomains)))
  712.     {
  713.         goto done;
  714.     }
  715.  
  716.     for (i = 0; i < numDomains; i++)
  717.     {
  718.         if (NULL != (pDom = virDomainLookupByID (pVmm, domainIds[i])))
  719.         {
  720.             processDomain (pDom);
  721.         }
  722.     }
  723.  
  724. done:
  725.     if (domainIds)
  726.     {
  727.         free (domainIds);
  728.     }
  729.  
  730.     if (pVmm)
  731.     {
  732.         virConnectClose (pVmm);
  733.     }
  734.  
  735.     return 0;
  736. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement