VirtualBox

Changeset 69820 in vbox


Ignore:
Timestamp:
Nov 24, 2017 11:43:38 AM (7 years ago)
Author:
vboxsync
Message:

Debugger/Linux: Cope with KASLR being used when probing for a Linux guest

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Debugger/DBGPlugInLinux.cpp

    r69500 r69820  
    170170*   Defined Constants And Macros                                                                                                 *
    171171*********************************************************************************************************************************/
     172/** First kernel map address for 32bit Linux hosts (__START_KERNEL_map). */
     173#define LNX32_KERNEL_ADDRESS_START      UINT32_C(0xc0000000)
     174/** First kernel map address for 64bit Linux hosts (__START_KERNEL_map). */
     175#define LNX64_KERNEL_ADDRESS_START      UINT64_C(0xffffffff80000000)
    172176/** Validates a 32-bit linux kernel address */
    173177#define LNX32_VALID_ADDRESS(Addr)       ((Addr) > UINT32_C(0x80000000) && (Addr) < UINT32_C(0xfffff000))
     
    24122416
    24132417/**
     2418 * Probes for a Linux kernel starting at the given address.
     2419 *
     2420 * @returns Flag whether something which looks like a valid Linux kernel was found.
     2421 * @param   pThis               The Linux digger data.
     2422 * @param   pUVM                The user mode VM handle.
     2423 * @param   uAddrStart          The address to start scanning at.
     2424 * @param   cbScan              How much to scan.
     2425 */
     2426static bool dbgDiggerLinuxProbeWithAddr(PDBGDIGGERLINUX pThis, PUVM pUVM, RTGCUINTPTR uAddrStart, size_t cbScan)
     2427{
     2428    /*
     2429     * Look for "Linux version " at the start of the rodata segment.
     2430     * Hope that this comes before any message buffer or other similar string.
     2431     */
     2432    DBGFADDRESS KernelAddr;
     2433    DBGFR3AddrFromFlat(pUVM, &KernelAddr, uAddrStart);
     2434    DBGFADDRESS HitAddr;
     2435    int rc = DBGFR3MemScan(pUVM, 0, &KernelAddr, cbScan, 1,
     2436                           g_abLinuxVersion, sizeof(g_abLinuxVersion) - 1, &HitAddr);
     2437    if (RT_SUCCESS(rc))
     2438    {
     2439        char szTmp[128];
     2440        char const *pszX = &szTmp[sizeof(g_abLinuxVersion) - 1];
     2441        rc = DBGFR3MemReadString(pUVM, 0, &HitAddr, szTmp, sizeof(szTmp));
     2442        if (    RT_SUCCESS(rc)
     2443            &&  (   (   pszX[0] == '2'  /* 2.x.y with x in {0..6} */
     2444                     && pszX[1] == '.'
     2445                     && pszX[2] >= '0'
     2446                     && pszX[2] <= '6')
     2447                 || (   pszX[0] >= '3'  /* 3.x, 4.x, ... 9.x */
     2448                     && pszX[0] <= '9'
     2449                     && pszX[1] == '.'
     2450                     && pszX[2] >= '0'
     2451                     && pszX[2] <= '9')
     2452                 )
     2453            )
     2454        {
     2455            pThis->AddrKernelBase  = KernelAddr;
     2456            pThis->AddrLinuxBanner = HitAddr;
     2457            return true;
     2458        }
     2459    }
     2460
     2461    return false;
     2462}
     2463
     2464/**
     2465 * Probes for a Linux kernel which has KASLR enabled.
     2466 *
     2467 * @returns Flag whether a possible candidate location was found.
     2468 * @param   pThis               The Linux digger data.
     2469 * @param   pUVM                The user mode VM handle.
     2470 * @param   uAddrKernelStart    The first address the kernel is expected at.
     2471 */
     2472static bool dbgDiggerLinuxProbeKaslr(PDBGDIGGERLINUX pThis, PUVM pUVM, RTGCUINTPTR uAddrKernelStart)
     2473{
     2474    /**
     2475     * With KASLR the kernel is loaded at a different address at each boot making detection
     2476     * more difficult for us.
     2477     *
     2478     * The randomization is done in arch/x86/boot/compressed/kaslr.c:choose_random_location() (as of Nov 2017).
     2479     * At the end of the method a random offset is chosen using find_random_virt_addr() which is added to the
     2480     * kernel map start in the caller (the start of the kernel depends on the bit size, see LNX32_KERNEL_ADDRESS_START
     2481     * and LNX64_KERNEL_ADDRESS_START for 32bit and 64bit kernels respectively).
     2482     * The lowest offset possible is LOAD_PHYSICAL_ADDR which is defined in arch/x86/include/asm/boot.h
     2483     * using CONFIG_PHYSICAL_START aligned to CONFIG_PHYSICAL_ALIGN.
     2484     * The default CONFIG_PHYSICAL_START and CONFIG_PHYSICAL_ALIGN are both 0x1000000 no matter whether a 32bit
     2485     * or a 64bit kernel is used. So the lowest offset to the kernel start address is 0x1000000.
     2486     * The find_random_virt_addr() the number of possible slots where the kernel can be placed based on the image size
     2487     * is calculated using the following formula:
     2488     *    cSlots = ((KERNEL_IMAGE_SIZE - 0x1000000 (minimum) - image_size) / 0x1000000 (CONFIG_PHYSICAL_ALIGN)) + 1
     2489     *
     2490     * KERNEL_IMAGE_SIZE is 1GB for 64bit kernels and 512MB for 32bit kernels, so the maximum number of slots (resulting
     2491     * in the largest possible offset) can be achieved when image_size (which contains the real size of the kernel image
     2492     * which is unknown for us) goes to 0 and a 1GB KERNEL_IMAGE_SIZE is assumed. With that the biggest cSlots which can be
     2493     * achieved is 64. The chosen random offset is taken from a random long integer using kaslr_get_random_long() modulo the
     2494     * number of slots which selects a slot between 0 and 63. The final offset is calculated using:
     2495     *    offAddr = random_addr * 0x1000000 (CONFIG_PHYSICAL_ALIGN) + 0x1000000 (minimum)
     2496     *
     2497     * So the highest offset the kernel can start is 0x40000000 which is 1GB (plus the maximum kernel size we defined).
     2498     */
     2499    if (dbgDiggerLinuxProbeWithAddr(pThis, pUVM, uAddrKernelStart, _1G + LNX_MAX_KERNEL_SIZE))
     2500        return true;
     2501
     2502    return false;
     2503}
     2504
     2505/**
    24142506 * @copydoc DBGFOSREG::pfnInit
    24152507 */
     
    24622554    PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
    24632555
    2464     /*
    2465      * Look for "Linux version " at the start of the rodata segment.
    2466      * Hope that this comes before any message buffer or other similar string.
    2467      */
    24682556    for (unsigned i = 0; i < RT_ELEMENTS(g_au64LnxKernelAddresses); i++)
    24692557    {
    2470         DBGFADDRESS KernelAddr;
    2471         DBGFR3AddrFromFlat(pUVM, &KernelAddr, g_au64LnxKernelAddresses[i]);
    2472         DBGFADDRESS HitAddr;
    2473         int rc = DBGFR3MemScan(pUVM, 0, &KernelAddr, LNX_MAX_KERNEL_SIZE, 1,
    2474                                g_abLinuxVersion, sizeof(g_abLinuxVersion) - 1, &HitAddr);
    2475         if (RT_SUCCESS(rc))
    2476         {
    2477             char szTmp[128];
    2478             char const *pszX = &szTmp[sizeof(g_abLinuxVersion) - 1];
    2479             rc = DBGFR3MemReadString(pUVM, 0, &HitAddr, szTmp, sizeof(szTmp));
    2480             if (    RT_SUCCESS(rc)
    2481                 &&  (   (   pszX[0] == '2'  /* 2.x.y with x in {0..6} */
    2482                          && pszX[1] == '.'
    2483                          && pszX[2] >= '0'
    2484                          && pszX[2] <= '6')
    2485                      || (   pszX[0] >= '3'  /* 3.x, 4.x, ... 9.x */
    2486                          && pszX[0] <= '9'
    2487                          && pszX[1] == '.'
    2488                          && pszX[2] >= '0'
    2489                          && pszX[2] <= '9')
    2490                      )
    2491                 )
    2492             {
    2493                 pThis->AddrKernelBase  = KernelAddr;
    2494                 pThis->AddrLinuxBanner = HitAddr;
    2495                 return true;
    2496             }
    2497         }
    2498     }
     2558        if (dbgDiggerLinuxProbeWithAddr(pThis, pUVM, g_au64LnxKernelAddresses[i], LNX_MAX_KERNEL_SIZE))
     2559            return true;
     2560    }
     2561
     2562    /* Maybe the kernel uses KASLR. */
     2563    if (dbgDiggerLinuxProbeKaslr(pThis, pUVM, LNX32_KERNEL_ADDRESS_START))
     2564        return true;
     2565
     2566    if (dbgDiggerLinuxProbeKaslr(pThis, pUVM, LNX64_KERNEL_ADDRESS_START))
     2567        return true;
     2568
    24992569    return false;
    25002570}
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette