Jump to content

AptioMemoryFix


vit9696
595 posts in this topic

Recommended Posts

 

Here comes an explanation of when KASLR is used, and how it is determined by the recent boot.efi.

 

First of all, here are the key combinations, most of which everybody knows about, but some are probably less well-known:

 

Cmd + V         — verbose (-v)
Cmd + S         — single mode (-s)
Cmd + S + Minus — kaslr off (if CSR_ALLOW_UNRESTRICTED_NVRAM, otherwise single mode)
Cmd + X         — boot mode x (I guess even authors of macosxbootloader are unaware of it)
Cmd + R         — recovery boot
Cmd + C + Minus — disable board compatibility check (-no_compat_check)
Cmd + K         — use prelinkedkernel.development instead of prelinkedkernel (kcsuffix=development)
Shift           — safe mode & kaslr off (-x)
In addition to a dedicated key combination, that is responsible for disabling KASLR, there are three more places to disable KASLR.

1. Cmd + S + Minus (only if CSR_ALLOW_UNRESTRICTED_NVRAM is set)

2. Shift (key combination for safe mode)

3. -x (boot argument for safe mode)

4. If prelinkedkernel header has 'comp' magic and its version is 0 (this thing)

 

Additionally to that I have to admit that slide=X boot argument takes no effect when CSR_ALLOW_UNRESTRICTED_NVRAM is no set.

For how KASLR address calculation works itself it is better to show the reverse-engineered code, because the exact offset calculation depends on CPU family and model.

 

 

 

void ParseBootArgs(…) {
...
    if ( (BootMode & 0x4001) == 0x4001 )        // BOOT_MODE_SAFE | BOOT_MODE_ASLR (-x)
    {
      DisableKaslr = 1;
      GlobalVariableState = BootMode & ~0x4000ui64;// & ~BOOT_MODE_ASLR
      goto KASLR_DONE;
    }
    if ( !DisableKaslr
      && !ReadBootArgument(*kernelCommandLine, "slide", &value, &valueLength)// == EFI_SUCCESS
      && CsrAllowUnrestrictedNvram() )
    {
      if ( !valueLength )                       // slide=
      {
        GenerateRandomSlideNumber();            // Also called very early at boot.efi start
SLIDE_SET:
        BYTE1(GlobalVariableState) |= 0x40u;    // |= BOOT_MODE_ASLR
        goto KASLR_DONE;
      }
      SuppliedSlide = StringToInteger(value);
      if ( SuppliedSlide )
      {
        if ( SuppliedSlide - 1 > 0xFE )         // SuppliedSlide >= 0x00 && SuppliedSlide <= 0xFF
          goto KASLR_DONE;
        gKernelSlide = (unsigned int)CalculateSlideAddress(SuppliedSlide);
        goto SLIDE_SET;
      }
      BYTE1(GlobalVariableState) &= 0xBFu;      // &= ~BOOT_MODE_ASLR
    }
KASLR_DONE:
...
}
 
UINT32 __cdecl CsrAllowUnrestrictedNvram()
{
  UINTN Size; // [rsp+28h] [rbp-8h]
 
  Size = 0i64;
  if ( !(HasCsrActiveConfig & 1) )
  {
    Size = 4i64;
    if ( (gRT->GetVariable((CHAR16 *)L"csr-active-config", &gAppleNvramVariableGuid, 0i64, &Size, &CsrActiveConfig) & 0x8000000000000000ui64) == 0i64 )
      HasCsrActiveConfig = 1;
  }
  return CsrActiveConfig & 0x40;
}
 
void __cdecl GenerateRandomSlideNumber()
{
  int RDRANDSupported; // esi
  UINT64 Slide; // rax
  UINT16 RDTSC; // ax
 
  _RAX = 1i64;
  __asm { cpuid }
  RDRANDSupported = _RCX & 0x40000000;
  do
  {
    if ( RDRANDSupported )
    {
      Slide = RDRandRandomValue(0);
      if ( Slide )
        break;
    }
    RDTSC = RDTSCCurrentValue();
    Slide = (unsigned __int8)(RDTSC ^ HIBYTE(RDTSC));
  }
  while ( !Slide );
  gKernelSlide = (unsigned int)CalculateSlideAddress(Slide);
}
 
UINT64 __fastcall CalculateSlideAddress(UINT8 Slide)
{
  UINT32 SlideInt; // er8
  UINT64 RealSlideAddress; // rax
  unsigned int CpuFamily; // edx
  int CpuModel; // er9
  UINT32 SlideAddress; // er8
 
  SlideInt = Slide;
  if ( (Slide & 0x80u) == 0 )
    return (unsigned int)Slide << 21;
  _RAX = 1i64;
  __asm { cpuid }
  CpuFamily = ((unsigned int)_RAX >> 8) & 0xF;
  if ( CpuFamily == 0xF )
    CpuFamily = (unsigned __int8)((unsigned int)_RAX >> 20) + 15;// Use ExtendedFamily
  CpuModel = (unsigned __int8)_RAX >> 4;
  if ( CpuFamily == 15 || CpuFamily == 6 )
    CpuModel |= ((unsigned int)_RAX >> 12) & 0xF0;// Use ExtendedModel
  SlideAddress = SlideInt << 21;
  RealSlideAddress = SlideAddress + 0x10200000;
  if ( (CpuModel | 0x10) != 0x3A )              // For CPUs which model is not 0x3A
    RealSlideAddress = SlideAddress;
  if ( CpuFamily != 6 )                         // For CPUs which family is not 6
    RealSlideAddress = SlideAddress;
  return RealSlideAddress;
}

 

 

The above makes me rather worried what should the approach be regarding slide=X usage for 2 cases:

1. Safe mode compatibility

2. System Integrity Protection compatibility (nvram restriction)

 

 

To fix issue #2 it is enough to override gBS->GetVariable function that will report csr-active-config to exist and have CSR_ALLOW_UNRESTRICTED_NVRAM bit set. This makes good sense to me, because the kernel will still have protected nvram and this SIP restrictions, but boot.efi allows us to set custom KASLR.

 

To fix #1 I would suggest to use a boot.efi patch, that will enable ASLR back. 01 40 00 00 is a reasonably rarely used constant (only 4 entries in the current boot.efi, two of which we need), and it has not changed since macosxbootloader. For this reason I consider a lookup of two 01 40 00 00 byte sequences being near each other (since the registers may change) to be quite a future-proof solution (although not pretty of course). Just patch them with FF FF FF FF and you will have KASLR in -x mode.

  • Like 12
Link to comment
Share on other sites

I have some questions about that code..... I know............... But still...

  if ( (Slide & 0x80u) == 0 )
    return (unsigned int)Slide << 21;

This tests Slide <= 128, if it is then it just multiplies by 0x200000. Which ranges from 0x10000000 to 0x1FE00000.

  SlideAddress = SlideInt << 21;
  RealSlideAddress = SlideAddress + 0x10200000;

For > 128, there are two parts. First, it gets multiplied by 0x200000 and added to 0x10200000. Which ranges from 0x10200000 to 0x20000000.

But the next part does not make sense.....

  if ( (CpuModel | 0x10) != 0x3A )              // For CPUs which model is not 0x3A
    RealSlideAddress = SlideAddress;

So for some reason anything that is not SB or IB the range becomes 0x200000 to 0xFE00000..... Ummmmmmmmmmmmm....... There's no way that slide=0 is putting the kernel at adresss 0.

 

EDIT: Slide=0 is not a valid slide so it will never be used to calculate the address.

  if ( CpuFamily != 6 )                         // For CPUs which family is not 6
    RealSlideAddress = SlideAddress;

This is not ever happening. What CPU is not family 6 that can run macOS?

      SuppliedSlide = StringToInteger(value);
      if ( SuppliedSlide )
      {
        if ( SuppliedSlide - 1 > 0xFE )         // SuppliedSlide >= 0x00 && SuppliedSlide <= 0xFF
          goto KASLR_DONE;
        gKernelSlide = (unsigned int)CalculateSlideAddress(SuppliedSlide);
        goto SLIDE_SET;
      }

What is this? This says slide=0 has no effect but it clearly does. Since if SuppliedSlide is zero it will be skipped for being false in the first if. The second if is the weirdest (and possibly dumbest) check that SuppliedSlide is <= 0xFF I've ever seen.

 

EDIT: Oh I see, slide=0 actually disables KASLR and it will go back to 0x100000.

         BYTE1(GlobalVariableState) &= 0xBFu;      // &= ~BOOT_MODE_ASLR

EDIT2: This is a disaster actually. I wonder if this has changed since the original KASLR because I remember dmazar explaining it to me exactly like I've been telling everyone to calculate it.... But that is not correct for SB and IB.

 

EDIT3: So the addresses available to be loaded at are 0x100000 for no KASLR. 0x200000 to 0x1FE00000 for non SB or IB. For SB and IB, 0x10200000 to 0x20000000. All aligned on 0x200000 boundaries.

 

EDIT4: In case anyone is wondering that's only 383 possible places the kernel could be located, if you don't know the CPU model. If you do then it's only 256 places, obviously. And one of those places (with KASLR disabled) you should attempt to attack first anyway, so it's actually one less. I don't see how this couldn't easily be attacked, it's not really randomized at all. In fact, I actually am not going to continue my thought...................

 

EDIT5: I'm dumb and did it backwards, vit9696 fixed the slide table below.

  • Like 5
Link to comment
Share on other sites

apianti, I suppose it is much easier to read this by comparing to the relevant macosxbootloader code: link.

 

It indeed is the case that CPUs newer than Ivy Bridge get a different slide offset, and I have no idea of the reasons behind. By the way, you got it a little wrong regarding zero, because slide setup is not called with any value but 1~255. So the minimum value is 0x100000. And with zero it falls through and BOOT_MODE_ASLR bit is unset.

 

I should porbably mention the relevant XNU code describing the use of kaslr, here it confirms to us that kaslr slide has to be 0x100000 due to the subtraction:

vm_offset_t
ml_static_ptovirt(
	vm_offset_t paddr)
{
	return (vm_offset_t)(((unsigned long) paddr) | VM_MIN_KERNEL_ADDRESS); // VM_MIN_KERNEL_ADDRESS = 0xFFFFFF8000000000
} 

static_base_address = ml_static_ptovirt(KERNEL_BASE_OFFSET); // KERNEL_BASE_OFFSET = 0x100000
base_address        = ml_static_ptovirt(args->kaddr);
vm_kernel_slide     = base_address - static_base_address;
There is nothing interesting/relevant after DONE, there are other boot arguments handling and stuff.

 

Regarding CPU family comparison I suppose it is legacy, since in old times Intel did have 0xF used in some of Xeons and Pentiums: https://software.intel.com/en-us/articles/intel-architecture-and-processor-identification-with-cpuid-model-and-family-numbers

From the top of my head I could say that Xserve1,1 had a Xeon Woodcrest processor, which had 0xF family.

  • Like 4
Link to comment
Share on other sites

apianti, I suppose it is much easier to read this by comparing to the relevant macosxbootloader code: link.

 

It indeed is the case that CPUs newer than Ivy Bridge get a different slide offset, and I have no idea of the reasons behind. By the way, you got it a little wrong regarding zero, because slide setup is not called with any value but 1~255. So the minimum value is 0x100000. And with zero it falls through and BOOT_MODE_ASLR bit is unset.

 

Yeah, I realized that and edited it because apparently slide=0 means disable KASLR before your response.

 

 

I should porbably mention the relevant XNU code describing the use of kaslr, here it confirms to us that kaslr slide has to be 0x100000 due to the subtraction:

vm_offset_t
ml_static_ptovirt(
	vm_offset_t paddr)
{
	return (vm_offset_t)(((unsigned long) paddr) | VM_MIN_KERNEL_ADDRESS); // VM_MIN_KERNEL_ADDRESS = 0xFFFFFF8000000000
} 

static_base_address = ml_static_ptovirt(KERNEL_BASE_OFFSET); // KERNEL_BASE_OFFSET = 0x100000
base_address        = ml_static_ptovirt(args->kaddr);
vm_kernel_slide     = base_address - static_base_address;

 

Oh, so it does need that 0x100000 added? Or subtracted? I think that confused me more.... The base_address is some different value though unless slide=0 or KASLR is disabled? Because vm_kernel_slide will be zero if it's disabled?

 

There is nothing interesting/relevant after DONE, there are other boot arguments handling and stuff.

 

Didn't think so but just making sure.

 

Regarding CPU family comparison I suppose it is legacy, since in old times Intel did have 0xF used in some of Xeons and Pentiums: https://software.intel.com/en-us/articles/intel-architecture-and-processor-identification-with-cpuid-model-and-family-numbers

From the top of my head I could say that Xserve1,1 had a Xeon Woodcrest processor, which had 0xF family.

 

Yeah but KASLR wasn't introduced until ML and Xserve1,1 can't run ML, it's not a supported model. So not sure who is writing that code but....... I think they may need some time off. Jobs ghost still making them pull 56 hour shifts?

  • Like 3
Link to comment
Share on other sites

Oh, so it does need that 0x100000 added? Or subtracted? I think that confused me more.... The base_address is some different value though unless slide=0 or KASLR is disabled? Because vm_kernel_slide will be zero if it's disabled?

 

I hope I understand it right, and it means the following.

 

The kernel is normally allocated at base 0x100000 + slide address.

Slide address consists of slide value (either passed by slide=X or implicitly generated), which always adds as X * 0x200000 and possibly a platform-specific & value-specific constant.

Here is the table with the relevant values.

 

For slide=0 (or when implicitly off) this is just 0x100000 (and thus people cannot boot if the address is used in -x).

For slide=0x1~0x7F (passed or randomly generated) the kernel is allocated from 0x1000000x200000 till 0x1000000xFE00000.

For slide=0x80~0xFF (passed or randomly generated) on Sandy Bridge or Ivy Bridge CPUs from 0x100000 + 0x20200000 till 0x100000 + 0x30000000.

For slide=0x80~0xFF (passed or randomly generated) on Other CPUs from 0x100000 + 0x10000000 till 0x100000 + 0x1FE00000.

  • Like 16
Link to comment
Share on other sites

Yeah, I just realized I did it backwards... Nice catch. I crossed mine out. And that made it way more understandable. I guess the original calculation was correct if the value < 128. For >= 128, it is not for SB or IB but the others it is, yes?

Link to comment
Share on other sites

Yeah, I just realized I did it backwards... Nice catch. I crossed mine out. And that made it way more understandable. I guess the original calculation was correct if the value < 128. For >= 128, it is not for SB or IB but the others it is, yes?

 

Not impossible indeed. Added to the first post just in case.

Link to comment
Share on other sites

Hi

can you explain the formula for calculating slide value?

can't understand what slide value to use with this memory map

Thanks in advance

 

 
Type       Start            End               # Pages          Attributes
available  0000000000000000-0000000000057FFF  0000000000000058 000000000000000F
reserved   0000000000058000-0000000000058FFF  0000000000000001 000000000000000F
available  0000000000059000-000000000009DFFF  0000000000000045 000000000000000F
reserved   000000000009E000-000000000009FFFF  0000000000000002 000000000000000F
available  0000000000100000-00000000A9845FFF  00000000000A9746 000000000000000F
LoaderData 00000000A9846000-00000000A9945FFF  0000000000000100 000000000000000F
available  00000000A9946000-00000000ABF1FFFF  00000000000025DA 000000000000000F
LoaderData 00000000ABF20000-00000000ABF33FFF  0000000000000014 000000000000000F
LoaderCode 00000000ABF34000-00000000AC000FFF  00000000000000CD 000000000000000F
ACPI_NVS   00000000AC001000-00000000AC007FFF  0000000000000007 000000000000000F
BS_data    00000000AC008000-00000000AC198FFF  0000000000000191 000000000000000F
BS_code    00000000AC199000-00000000ACF0AFFF  0000000000000D72 000000000000000F
BS_data    00000000ACF0B000-00000000ACF1BFFF  0000000000000011 000000000000000F
BS_code    00000000ACF1C000-00000000ACF2DFFF  0000000000000012 000000000000000F
BS_data    00000000ACF2E000-00000000ACF38FFF  000000000000000B 000000000000000F
BS_code    00000000ACF39000-00000000ACF3AFFF  0000000000000002 000000000000000F
RT_data    00000000ACF3B000-00000000AD7F6FFF  00000000000008BC 800000000000000F
BS_data    00000000AD7F7000-00000000AD835FFF  000000000000003F 000000000000000F
LoaderData 00000000AD836000-00000000AD844FFF  000000000000000F 000000000000000F
available  00000000AD845000-00000000C0F8DFFF  0000000000013749 000000000000000F
BS_data    00000000C0F8E000-00000000C128EFFF  0000000000000301 000000000000000F
available  00000000C128F000-00000000C158FFFF  0000000000000301 000000000000000F
BS_data    00000000C1590000-00000000C16BCFFF  000000000000012D 000000000000000F
available  00000000C16BD000-00000000C174AFFF  000000000000008E 000000000000000F
BS_data    00000000C174B000-00000000C58FAFFF  00000000000041B0 000000000000000F
available  00000000C58FB000-00000000C6C28FFF  000000000000132E 000000000000000F
BS_code    00000000C6C29000-00000000C6FF2FFF  00000000000003CA 000000000000000F
reserved   00000000C6FF3000-00000000C7023FFF  0000000000000031 000000000000000F
reserved   00000000C7024000-00000000C7084FFF  0000000000000061 000000000000000F
available  00000000C7085000-00000000C70ECFFF  0000000000000068 000000000000000F
ACPI_NVS   00000000C70ED000-00000000C7215FFF  0000000000000129 000000000000000F
ACPI_NVS   00000000C7216000-00000000C7219FFF  0000000000000004 000000000000000F
ACPI_NVS   00000000C721A000-00000000C7231FFF  0000000000000018 000000000000000F
RT_data    00000000C7232000-00000000C9C3CFFF  0000000000002A0B 800000000000000F
RT_data    00000000C9C3D000-00000000C9E95FFF  0000000000000259 800000000000000F
RT_data    00000000C9E96000-00000000C9E97FFF  0000000000000002 800000000000000F
RT_data    00000000C9E98000-00000000C9F16FFF  000000000000007F 800000000000000F
RT_code    00000000C9F17000-00000000C9F60FFF  000000000000004A 800000000000000F
RT_code    00000000C9F61000-00000000C9FFEFFF  000000000000009E 800000000000000F
BS_data    00000000C9FFF000-00000000C9FFFFFF  0000000000000001 000000000000000F
available  0000000100000000-000000042FDFFFFF  000000000032FE00 000000000000000F
reserved   00000000CB000000-00000000CF1FFFFF  0000000000004200 8000000000000000
MemMapIO   00000000F8000000-00000000FBFFFFFF  0000000000004000 8000000000000001
MemMapIO   00000000FEC00000-00000000FEC00FFF  0000000000000001 8000000000000001
MemMapIO   00000000FED00000-00000000FED03FFF  0000000000000004 8000000000000001
MemMapIO   00000000FED1C000-00000000FED1FFFF  0000000000000004 8000000000000001
MemMapIO   00000000FEE00000-00000000FEE00FFF  0000000000000001 8000000000000001
MemMapIO   00000000FF000000-00000000FFFFFFFF  0000000000001000 8000000000000001
Link to comment
Share on other sites

 

Hi

can you explain the formula for calculating slide value?

can't understand what slide value to use with this memory map

Thanks in advance

available  0000000000100000-00000000A9845FFF  00000000000A9746 000000000000000F

 

Any slide will be fine, so don't set one and let it choose one randomly.

  • Like 1
Link to comment
Share on other sites

I got a strange result

OsxAptioFix2 error: Doing hibernate wake, but did not find hibernate image address.... waiting 5 secs ...

Meanwhile NVRAM works. Thanks!

 

PS.

wake-failure	%013%08f
Link to comment
Share on other sites

Report:

 

AptioInputFix may cause CloverGUI can’t move through the keyboard.

 

It may cause CloverGUI Freeze.

 

 

从我的 iPhone 发送,使用 Tapatalk

64dda9dfedbaf513f909322577c84a79.jpg

 

My another laptop using aptiomemfix and show this.

 

 

从我的 iPhone 发送,使用 Tapatalk

Link to comment
Share on other sites

Thank you

It is working well for my rig

I have deleted latest optiofix2 and slide=128 (mandatory for me to pass memory error in some conditions) and system starts fine

I have some debug message from two new efi you posted, maybe it is a scan of memory position to automatic decide which slide is useful for me...

Just compile and had not test my self

Link to comment
Share on other sites

May we somehow include this driver into Clover release package as option?

Why not? But I would suggest you wait a little, perhaps some ideas arise from other devs and they could improve things first.

 

2all,

AptioInputFix aka AsAmiShim is the source code of my FV2 input driver, you may not need it.

  • Like 5
Link to comment
Share on other sites

May we somehow include this driver into Clover release package as option?

 

I'm reviewing the code and will probably replace what's in clover if it looks good. But I want a new name, I hate referring to it as "Aptio" fix, it is for more than just that though... Philip will need to change the package, I hate that thing, won't touch it....

  • Like 1
Link to comment
Share on other sites

apianti, it is literally for aptio, and there is one more driver, which is also for aptio. So the naming if anything should stay.

In my opinion, it is better to maintain it externally from clover and just clone as edk and friends, because there is no reason only clover should use these drivers.

I am fine to give any of you commit access if necessary.

  • Like 2
Link to comment
Share on other sites

×
×
  • Create New...