📜 ⬆️ ⬇️

Internal ASLR device in Windows 8

ASLR is the Address Space Layout Randomization, address space randomization. This is a security mechanism that includes the randomization of virtual memory addresses of various data structures that are sensitive to attacks. The location in the memory of the target structure is difficult to predict, so the attacker's chances for success are small.

The implementation of ASLR in Windows is closely related to the relocation mechanism of the executable images. Relocation allows a PE file to be loaded not only on a fixed preferred base. The relocation section in the PE file is a key structure when moving an image. It describes what changes need to be made to certain elements of the code and data to ensure the correct functioning of the application at a different base address.

A key role in the work of ASLR is played by a random number generator and a couple of functions that modify the base address of the loaded PE file.
')
Windows 8 relies on a random number generator, which is essentially a Fibonacci delayed generator with parameters j = 24 and k = 55. Its kernel is initialized in the winload.exe module at system startup. Winload.exe collects entropy from various sources: registry keys, TPM, current time, ACPI, as well as with the help of the new rdrand instruction. The initialization of a nuclear random number generator is described in detail in the source [1].

Let us consider the new rdrand instruction in more detail. In processors based on the Ivy Bridge architecture, Intel Secure Key technology was introduced to generate high-quality pseudo-random numbers. It is implemented using a hardware digital random number generator (DRNG) and rdrand instructions for programmatically extracting values ​​from it.

From a hardware point of view, DRNG is a separate module on a processor chip. It works asynchronously with the processor cores at 3 GHz. DRNG uses thermal noise as a source of entropy; in addition, it has a built-in testing system that performs a series of quality checks on the extracted random values. In the event of an unsatisfactory test result, the DRNG stops generating random values.

To extract random numbers from DRNG, use the rdrand instruction. The documentation for it notes that, in theory, the DRNG can return zero values ​​in the event of an unsatisfactory test result or empty internal queue of random values. However, in practice, it was not possible to empty the DRNG.

The Intel Secure Key is a powerful random number generator that produces high quality random values ​​at very high speeds. It is practically impossible to predict the initial state of the generator, initialized using the rdrand instruction (unlike other sources of entropy).

The internal interface function of a nuclear PRNG is ExGenRandom (). It also has an exported wrapper function RtlRandomEx (). ASLR in Windows 8 uses ExGenRandom () - unlike previous versions that relied on the rdtsc instruction. The latter is used to obtain a time counter on the CPU, which varies linearly and, therefore, cannot ensure the proper quality of the generated values, which is unsafe.

The main function of the ASLR mechanism is MiSelectImageBase (). In Windows 8, it can be described by the following pseudocode.

#define MI_64K_ALIGN(x) (x + 0x0F) >> 4 #define MmHighsetUserAddress 0x7FFFFFEFFFF typedef PIMAGE_BASE ULONG_PTR; typedef enum _MI_MEMORY_HIGHLOW { MiMemoryHigh = 0, MiMemoryLow = 1, MiMemoryHighLow = 2 } MI_MEMORY_HIGHLOW, *PMI_MEMORY_HIGHLOW; MI_MEMORY_HIGHLOW MiSelectBitMapForImage(PSEGMENT pSeg) { if (!(pSeg->SegmentFlags & FLAG_BINARY32)) // WOW binary { if (!(pSeg->ImageInformation->ImageFlags & FLAG_BASE_BELOW_4GB)) { if (pSeg->BasedAddress > 0x100000000) { return MiMemoryHighLow; } else { return MiMemoryLow; } } } return MiMemoryHigh; } PIMAGE_BASE MiSelectImageBase(void* a1<rcx>, PSEGMENT pSeg) { MI_MEMORY_HIGHLOW ImageBitmapType; ULONG ImageBias; RTL_BITMAP *pImageBitMap; ULONG_PTR ImageTopAddress; ULONG RelocationSizein64k; MI_SECTION_IMAGE_INFORMATION *pImageInformation; ULONG_PTR RelocDelta; PIMAGE_BASE Result = NULL; // rsi = rcx // rcx = rdx // rdi = rdx pImageInformation = pSeg->ImageInformation; ImageBitmapType = MiSelectBitMapForImage(pSeg); a1->off_40h = ImageBitmapType; if (ImageBitmapType == MiMemoryLow) { // 64-bit executable with image base below 4 GB ImageBias = MiImageBias64Low; pImageBitMap = MiImageBitMap64Low; ImageTopAddress = 0x78000000; } else { if (ImageBitmapType == MiMemoryHighLow) { // 64-bit executable with image base above 4 GB ImageBias = MiImageBias64High; pImageBitMap = MiImageBitMap64High; ImageTopAddress = 0x7FFFFFE0000; } else { // MiMemoryHigh 32-bit executable image ImageBias = MiImageBias; pImageBitMap = MiImageBitMap; ImageTopAddress = 0x78000000; } } // pSeg->ControlArea->BitMap ^= (pSeg->ControlArea->BitMap ^ (ImageBitmapType << 29)) & 0x60000000; // or bitfield form pSeg->ControlArea.BitMap = ImageBitmapType; RelocationSizein64k = MI_64K_ALIGN(pSeg->TotalNumberOfPtes); if (pSeg->ImageInformation->ImageCharacteristics & IMAGE_FILE_DLL) { ULONG StartBit = 0; ULONG GlobalRelocStartBit = 0; StartBit = RtlFindClearBits(pImageBitMap, RelocationSizein64k, ImageBias); if (StartBit != 0xFFFFFFFF) { StartBit = MiObtainRelocationBits(pImageBitMap, RelocationSizein64k, StartBit, 0); if (StartBit != 0xFFFFFFFF) { Result = ImageTopAddress - (((RelocationSizein64k) + StartBit) << 0x10); if (Result == (pSeg->BasedAddress - a1->SelectedBase)) { GlobalRelocStartBit = MiObtainRelocationBits(pImageBitMap, RelocationSizein64k, StartBit, 1); StartBit = (GlobalRelocStartBit != 0xFFFFFFFF) ? GlobalRelocStartBit : StartBit; Result = ImageTopAddress - (RelocationSizein64k + StartBit) << 0x10; } a1->RelocStartBit = StartBit; a1->RelocationSizein64k = RelocationSizein64k; pSeg->ControlArea->ImageRelocationStartBit = StartBit; pSeg->ControlArea->ImageRelocationSizeIn64k = RelocationSizein64k; return Result; } } } else { // EXE image if (a1->SelectedBase != NULL) { return pSeg->BasedAddress; } if (ImageBitmapType == MiMemoryHighLow) { a1->RelocStartBit = 0xFFFFFFFF; a1->RelocationSizein64k = (WORD)RelocationSizein64k; pSeg->ControlArea->ImageRelocationStartBit = 0xFFFFFFFF; pSeg->ControlArea->ImageRelocationSizeIn64k = (WORD)RelocationSizein64k; return ((DWORD)(ExGenRandom(1) % (0x20001 - RelocationSizein64k)) + 0x7F60000) << 16; } } ULONG RandomVal = ExGenRandom(1); RandomVal = (RandomVal % 0xFE + 1) << 0x10; RelocDelta = pSeg->BasedAddress - a1->SelectedBase; if (RelocDelta > MmHighsetUserAddress) { return 0; } if ((RelocationSizein64k << 0x10) > MmHighsetUserAddress) { return 0; } if (RelocDelta + (RelocationSizein64k << 0x10) <= RelocDelta) { return 0; } if (RelocDelta + (RelocationSizein64k << 0x10) > MmHighsetUserAddress) { return 0; } if (a1->SelectedBase + RandomVal == 0) { Result = pSeg->BasedAddress; } else { if (RelocDelta > RandomVal) { Result = RelocDelta - RandomVal; } else { Result = RelocDelta + RandomVal; if (Result < RelocDelta) { return 0; } if (((RelocationSizein64k << 0x10) + RelocDelta + RandomVal) > 0x7FFFFFDFFFF) { return 0; } if (((RelocationSizein64k << 0x10) + RelocDelta + RandomVal) < (RelocDelta + (RelocationSizein64k << 0x10)))) { return 0; } } } //random_epilog a1->RelocStartBit = 0xFFFFFFFF; a1->RelocationSizein64k = RelocationSizein64k; pSeg->ControlArea->ImageRelocationStartBit = 0xFFFFFFFF; pSeg->ControlArea->ImageRelocationSizeIn64k = RelocationSizein64k; return Result; } 

As you can see, there are three different bitmap images. The first is for 32-bit executable applications, the second is for 64-bit applications, the third is for 64-bit applications with a base higher than 4 GB, which gives them a virtual address with high entropy.

Randomization of executable images directly affects their base address. In the case of loading libraries, ASLR is part of the module relocation process: a random variable when choosing a new base address is the ImageBias variable, which is initialized at system start.

 VOID MiInitializeRelocations() { MiImageBias = ExGenRandom(1) % 256; MiImageBias64Low = ExGenRandom(1) % MiImageBitMap64Low.SizeOfBitMap; MiImageBias64High = ExGenRandom(1) % MiImageBitMap64High.SizeOfBitMap; return; } 

Bitmaps of executable images display the address space of current user processes. The executable image gets the base address at boot time, it will be re-loaded into other processes at the same base address. This behavior of the loader is most effective in terms of speed and memory saving due to the use of copy-on-write mechanism by executable images.

The current implementation of ASLR in Windows 8 allows you to randomize the base address of applications that do not support randomization. Below is a table showing the loader behavior depending on the combinations of linker flags associated with ASLR.



* It is impossible to build an image using MSVS, because the / DYNAMICBASE flag requires the / FIXED: NO flag, which generates a relocation section

You can notice a change in the bootloader behavior characteristic of Windows 8: if the executable image has a relocation section, then it will be loaded anyway. This is further evidence of the relationship between the ASLR and the relocation mechanism.

In general, it can be said that the implementation of new ASLR functions in Windows 8 does not have a significant impact on the logic of the code, so it is rather difficult to detect useful vulnerabilities in it. Increasing entropy for randomizing various objects is essentially a replacement for the constant expression in the code. In addition, the code columns can be noted inspection code.

Sources
[1] Valasek Ch., Mandt T. Windows 8 Heap Internals. 2012
[2] Johnson K., Miller M. Exploit Mitigation Improvements in Windows 8. Slides, Black Hat USA, 2012.
[3] Intel. IntelDigital Random Number Generator (DRNG): Software Implementation Guide. Intel Corporation, 2012.
[4] Analysis of the Address Space Layout. Symantec Advances Threat Research, 2007.
[5] Sotirov A., Dowd M. Bypassing Browser Memory Protections. 2008

Authors: Artem Shishkin ( honorarybot ) and Ilya Smith ( blackzert ), Research Center Positive Research.

Source: https://habr.com/ru/post/162617/


All Articles