==Phrack Inc.== Volume 0x10, Issue 0x46, Phile #0x07 of 0x0f |=-----------------------------------------------------------------------=| |=--------------------=[ Viewer Discretion Advised: ]=-------------------=| |=--------------=[ (De)coding an iOS Kernel Vulnerability ]=-------------=| |=-----------------------------------------------------------------------=| |=-------------------------=[ Adam Donenfeld ]=--------------------------=| |=----------------------------=[ @doadam ]=------------------------------=| |=-----------------------------------------------------------------------=| --[ Table of contents 0) Introduction 1) Sandbox concepts 2) A bug - how it all got started (IOSurface) 3) A bug - finding a primitive for the IOSurface bug 4) Tracing the iOS kernel 5) Reversing AppleD5500.kext 6) Influencing AppleD5500.kext via mediaserverd 7) _The_ bug 8) H.264 in general and in iOS 9) mediaserverd didn't read the fucking manual 0xA) Takeaways 0xB) Final words 0xC) References 0xD) Code --[ 0 - Introduction The goal of this article is to demonstrate a (relatively) hard-to-reach attack surface on iOS, and showing the entire process from the beginning of the research till the point where a vulnerability is being found. While exploitation is out of the scope in this article, understanding the process of defining the attack surface, researching and while making your life easier (see sections 4 and 9), can provide beginners and expert hackers alike, a different approach for sandbox-accessible vulnerability research. The bug in question is CVE-2018-4109 [1], which was found by yours truly, that is Adam Donenfeld (@doadam). A PoC of the vulnerability is also available with this paper, and you're free to use it for educational purposes only. While an exploit can (IMO) be written for this vulnerability, I had too many things to do (writing this paper for instance) but if you feel like working on an exploit, feel free to write me if you want my help with it. Without further ado - let's start. --[ 1 - Sandbox concepts On all modern operating systems, most of the processes are by default restricted by sandbox technologies. A sandbox is an extra layer of protection, which prevents certain processes from accessing certain mechanisms. A sandbox is mandatory for many reasons, for instance: * Preventing leakage of sensitive information. For example, let's take the case where an attacker has breached your phone using a WebKit exploit. While WebKit can steal information related to your browsing, it will not be able to read your contacts, because the sandbox checks who tries to access your contacts, and denies permission unless there's a legitimate reason (If the attacker has gained code execution using a vulnerability in the Contacts app, he will probably be able to access your Contacts). * Narrowing attack surface. Most of the vulnerabilities out there can be found in different, unrelated components of the system (as will be shown soon). In the world of iOS, an interesting example is CVE-2015-7006 [2]. CVE-2015-7006 is a directory traversal which could be triggered via an Airdrop connection on iOS. The daemon in question was "sharingd". The directory traversal ultimately gave the attacker a write file primitive, meaning an attacker could overwrite any file on the system. Because sharingd was running unsandboxed as root back then, this vulnerability alone was enough to do various powerful operations (an installation of an arbitrary app, for example). Apple has since sandboxed sharingd. If sharingd would have been sandboxed before the publication of CVE-2015-7006, the vulnerability alone wouldn't be so powerful, because the primitives and privileges which could be gained are substantially limited (after being sandboxed, sharingd couldn't write any file on the system, thus couldn't manipulate installd to install arbitrary applications). While fixing vulnerabilities like the one in sharingd does solve the specific issue in CVE-2015-7006, that alone doesn't approach the main issue: any exploit in sharingd results in compromise of the entire device. As a result, a lot of vendors (Apple among them) designed their system so that almost everything is sandboxed, and nowadays, almost every operation that requires hardware interaction is sandboxed and is only given upon permission from the user\Apple. Because the vulnerability discussed in this paper (CVE-2018-4109) is in the accelerated hardware decoding driver, let's see the approach Apple took in sandboxing video operations, namely, video encoding\decoding: The following graph demonstrates how the app would interact with the video-decoding driver if there would be no sandbox: | EL0 (user mode) | EL1 (kernel mode) | +-------+ Decode video request | +---------------+ | |---------------------------+->| | |iOS app| | |AppleD5500.kext| | |<--------------------------+--| | +-------+ Decode video response | +---------------+ | | | | | Fortunately for Apple, communication with every hardware accelerated encoding\decoding driver is sandboxed, meaning each request goes through a "broker" (mediaserverd). This can be extremely time-consuming for an attacker, because it means the communication with AppleD5500.kext is defined by mediaserverd and that an unprivileged attacker can't access "exotic features" without having prior access to the driver (or code execution in a privileged context like mediaserverd). This is how unprivileged apps communicate with AppleD5500.kext: EL0 (user mode) | EL1 (kernel mode) | decode | +-------+ video +------------+ | new +---------------+ | | request | | |sanitized | | | |----------->| | | request | | |iOS app| |mediaserverd|-+---------->|AppleD5500.kext| | | | |<+-----------| | | |<-----------| | | Decode | | | | simplified | | |response | | +-------+ decode +------------+ | +---------------+ response | | | | | | | | _|_ \ / ' +-------------------------------------+ |* Basic video frame validation | |* Confined API | |* Check decoding\encoding permissions| +-------------------------------------+ As we can see from the diagram, not only mediaserverd sanitizes our request, it never forwards requests: in fact, it recreates the request\response accordingly, which limits the attacker's power, both in causing memory corruptions and performing infoleaks. --[ 2 - A bug - how it all got started (IOSurface) The bug was hidden deeply within the AppleD5500.kext file and while I had no intention to reverse engineer AppleD5500.kext, I found myself doing so in pursuit of a candidate for a different bug I found (which Apple silently fixed without issuing a CVE for). While the other bug is not in the scope of this article, in order to understand how CVE-2018-4109 was originally found, it is important to have some background on the other bug. That other bug was in a driver called IOSurface.kext. IOSurface are objects which primarily store framebuffers and pixels and information about these. IOSurface allows transferring a lot of information between processes about framebuffers without causing a lot of overhead. Each IOSurface object has an ID, which can be used as a request to an IOSurfaceRootUserClient object. IOSurfaces map the information between different processes, and thus save the overhead of sending a lot of information between processes. In iOS, a lot of drivers use IOSurface when it comes to graphics. The user doesn't store anything on the IOSurface object except for its ID. This means that in order to use an IOSurface object (for example, for video decoding), the user just needs to supply the ID to the appropriate driver and the original video is extracted from the IOSurface. The video itself is never being sent to the driver as a part of the request. IOSurface objects store a lot of properties about the graphics; one of them is called "plane". For brevity, there was a sign mismatch in the "offset" of the plane. It means that each driver which used the plane's offset (or base), would have had a negative int, while the kernel "IOSurface->getPlaneSize()" function regarded the plane's offset as a uint32_t. So this vulnerability resulted in a buffer overflow. Because surface objects only store that information without really "using" it (e.g performing memory manipulations based on the plane offset), it was necessary to find a different driver that used the plane's offset to actually perform a buffer overflow (or anything else which would give us more primitives). --[ 3 - A bug - finding a primitive for the IOSurface bug Fortunately, if a driver wants to use IOSurface objects, it has to find the "IOSurfaceRoot" service, which is public and is named in the kernel's registryas "IOCoreSurfaceRoot". This means that each driver who actually needs IOSurface will have the string "IOCoreSurfaceRoot". * Please note that IORegistry isn't within the scope of this paper. * You can however read about it in the following Apple's document: https://developer.apple.com/library/archive/documentation/DeviceDrivers/ \ Conceptual/IOKitFundamentals/TheRegistry/TheRegistry.html Looking up the string in IDA yields the following results: __PRELINK_TEXT:__PRELINK_TEXT_hidden: IOCoreSurfaceRoot com.apple.iokit.IOSurface:__cstring: IOCoreSurfaceRoot com.apple.driver.AppleM2ScalerCSC:__cstring: IOCoreSurfaceRoot com.apple.iokit.IOMobileGraphicsFamily:__cstring: IOCoreSurfaceRoot com.apple.driver.AppleD5500:__cstring: IOCoreSurfaceRoot com.apple.driver.AppleAVE:__cstring: IOCoreSurfaceRoot com.apple.drivers.AppleS7002SPUSphere:__cstring: IOCoreSurfaceRoot com.apple.driver.AppleAVD:__cstring: IOCoreSurfaceRoot com.apple.driver.AppleH10CameraInterface:__cstring: IOCoreSurfaceRoot com.apple.iokit.IOAcceleratorFamily:__cstring: IOCoreSurfaceRoot com.apple.iokit.IOAcceleratorFamily:__cstring: IOCoreSurfaceRoot Because Apple's drivers are mostly closed-source, it takes a lot of effort to understand how each driver uses the IOSurface objects. Therefore it was necessary (and just easy) to look for the string "plane" in each one of these drivers. While this doesn't guarantee we actually find anything useful, it's easy and it doesn't consume a lot of time. Fortunately, the following string came up (newlines added for readability): Assertion "outWidth > pIOSurfaceDst->getPlaneWidth(0) || outHeight > pIOSurfaceDst->getPlaneHeight(0) || outWidth < 16 || outHeight < 16 || inWidth < 16 || inHeight < 16" failed in "/BuildRoot/Library/Caches/com.apple.xbs/Sources/AppleD5500/ AppleD5500-165.5/AppleD5500.cpp" at line 3461 goto bail1 Around the usage of that string, there was the following assembly code: SXTW X2, W25 MOV W1, #0x80 MOV X0, X21 BL memset As AppleD5500.kext is closed-source, one needs to guess a lot, and try to infer from the code what is the context in each function. Because we search for the usage of an IOSurface object, which has a vtable, one useful thing would be to add a comment around every virtual call with the function name of the corresponding IOSurface object. Having this and our "plane" string in mind, we expect virtual calls which contain "plane" in their name. To find the vtable of IOSurface (or any vtable of any object in a kext), it is possible to reverse engineer the kext on a macOS. Kexts are still symbolicated on macOS and therefore it is possible to obtain meaningful names for the vtable entries. For the sake of this example, we'll reverse IOSurface.kext here. Opening up the IOSurface kext binary (on macOS it is located in the following path: /System/Library/Extensions/IOSurface.kext/Contents/MacOS/IOSurface), we get a symbolicated kext. To get the actual IOSurface's vtable, we can simply open the "Names" view (Shift+F4 for the keyboard shortcut lovers) and search for the string "vtable for'IOSurface". This will give us the offset-0x10 of the vtable, along with all the entries, symbolicated. Although sometimes the vtable entries are in a different order, they are virtually the same (minus the diff between ARM and Intel CPUs), so it is necessary to make sure you look at the same function and not just blindly picking up the name from the macOS version. This indeed works here: LDR X8, [X19] ; X8=IOSurface.vtable LDR X8, [X8,#0x110] ; X8=&IOSurface->getPlaneSize MOV W1, #0 MOV X0, X19 BLR X8 ; IOSurface->getPlaneSize(0) MOV X23, X0 LDR X8, [X19] ; X8=IOSurface.vtable LDR X8, [X8,#0x110] ; X8=&IOSurface->getPlaneSize MOV W1, #1 MOV X0, X19 BLR X8 ; IOSurface->getPlaneSize(1) MOV X25, X0 SXTW X2, W23 MOV W1, #0x80 LDR X0, [SP,#0x120+var_E0] BL memset ; memset(unk, 0x80, planeSize0) SXTW X2, W25 MOV W1, #0x80 MOV X0, X21 BL memset ; memset(unk, 0x80, planeSize1) So it looks as if we have a new primitive! We can arbitrarily overwrite something with 0x80, while we control the length of the overwrite. We do not control "unk" (which is later revealed that it is the mapping of the IOSurface object; keep reading). The length is taken from the plane member of something we assume is an IOSurface object, which we can arbitrarily control using the vulnerability in IOSurface.kext. Obviously this is a far fetched assumption. Except for the string we found, there's nothing else that hints this is indeed an IOSurface object. To verify that, it is necessary first to understand what AppleD5500 is. AppleD5500 is a video-decoding driver, which is not accessible from the default sandbox. Communication with this device is done solely via mediaserverd, as described in the infographic above (section 1). So the next objective is to see how to trigger the function with the IOSurface usage. The function is approximately 20 functions from the entry point to the driver's communication (AppleD5500::externalMethod) [3]. Apple does not provide us with the right tools to debug the iOS kernel (in fact, it constantly makes it more and more complicated), and macOS doesn't have this driver. While guessing can get you started, getting a deterministic code-flow is something that we want to assure, and not assume, as the direction of our research might be oriented based on such an assumption. --[ 4 - Tracing the iOS kernel I took Yalu102 [4] (thanks to @qwertyoruiopz and @macrograss for that) and utilized its KPP bypass. KPP [5] is a (not so new) mechanism that was introduced in iOS 9, checking the integrity of the text section of the kernel, meaning you can't modify the kernel's text section. I didn't care about setting breakpoints in the kernel, but I just wanted to get a dump of all the registers given a specific address. This would be enough to understand how to control the code-flow, or at least how to progress steadily towards the function which uses the plane from our IOSurface object (and understand whether this was actually an IOSurface object in the first place). What I did was as follows; assuming we want to see the registers' state at address 0x10C: Kernel code with no KPP ------------------ ADDRESS | | 0x100 | | ------------------ | | 0x104 | | ------------------ | | 0x108 | | ------------------ | | 0x10C | | ------------------ | | 0x110 | | ------------------ | | 0x114 | | ------------------ | | 0x118 | | ------------------ We overwrite 0x10 bytes with the following assembly code: LDR x16, #0x8 BLR x16 .quad shellcode_address shellcode_address contains code which prints the registers' state, a snippet from the shellcode: STP x0, x1 [SP] STP x2, x3 [SP, 0x10] ... LDR x0, debug_str LDR x16, kprintf BLR x16 MOV x0, x0 MOV x0, x0 MOV x0, x0 MOV x0, x0 RET Before overwriting 0x10C, the last 4 NOP instructions (MOV x0, x0) are replaced with the original instructions at 0x10C-0x11C. This way, the code executes seamlessly (as long as no branches are being replaced). x16 was chosen because according to the ABI, x16 is only used for jumping to stubs (so it is safe to overwrite it). This way we can see the registers' state at (almost) any address in the kernel without hurting performance or slowing the research. Generally speaking, I've found that this infrastructure work, as time consuming as it might be, will be insanely helpful later on, and always worths the invested time. Ultimately, the state of the kernel text will look like the following: ------------------ | LDR x16, #0x8 |0x1000 ------------------ | BLR x16 |0x1004 ------------------ | .quad shelladdr|0x1008 ------------------ ; memcpy(0x10C, 0x1000, 0x10) ADDRESS ------------------ | |0x100 | | ------------------ | |0x104 | | ------------------ | |0x108 | | ------------------ | LDR x16, #0x8 |0x10C | | ------------------ | BLR x16 |0x110 +---------------------+ | |------------------>|STP x0, x1 [SP] | shelladdr ------------------ |STP x2, x3 [SP, #0x8]| | .quad shelladdr|0x114 |... | | | |LDR x0, kdebug_str | ------------------ |LDR x16, kprintf | | |0x11C |BLR x16 | | |<+ |old insn from 0x10C | ------------------ | |old insn from 0x110 | | |... | +-----------------|RET | back to orig code +---------------------+ The shellcode advances X30 as well, so that we return to a valid instruction (0x10C-0x11C are not restored upon the shellcode's execution). At the time of this writing there are other (public) ways to achieve the same result, but that's what I did, and the most important point I'd like to show here is that infrastructure work is extremely important, and I think every decent researcher who has experience in the field, has written some tools\scripts to ease the research process. Besides, at the return\appearance of an AMCC bypass, this could sleep be handy ;) --[ 5 - Reversing AppleD5500.kext Continuing our research, we know that AppleD5500 has something to do with IOSurfaces. So the next step is to see where the driver actually looks up\fetches IOSurface objects based on their IDs. A quick string search reveals the following string: "AppleVXD393::allocateKernelMemory kAllocMapTypeIOSurface - lookupSurface failed. %d\n" Going to the place where this string is being used, I did the same thing - I added a comment near every virtual call to see where the driver probably uses IOSurface (you can probably guess by now that this is an automated script, another 'infrastructure' work :) ). This indeed looked like an IOSurface object, but to verify that for 100%, I used the same kernel tracing technique like before and checked the vtable of the object in use. This was indeed an IOSurface vtable! This means we know now where the IOSurface object is being looked up. IOSurface was stored exactly in the same offset used in our mysterious memset call. Using the kernel tracing technique we see that indeed this IOSurface object is used for the memset as well! So if we can control the IOSurface object we can do an arbitrary write. Unfortunately, at this point Apple silently fixed the IOSurface plane bug, but I got involved in this research deep enough to continue researching this area of AppleD5500. Now the next part is to make sure we control this IOSurface object. We can obviously do that assuming we magically have an AppleD5500 send right port, but perhaps we can influence mediaserverd to supply our own IOSurface object. --[ 6 - Influencing AppleD5500.kext via mediaserverd Reverse engineering mediaserverd and looking for calls to anything that looks like AppleD5500 yielded no results, but after further investigation (= symbols and strings search). I saw that VideoToolbox was responsible for video decoding, and thus I assumed it was responsible for AppleD5500 as well (though no mention of AppleD5500 was in VideoToolbox). When looking for AppleD5500 strings in the entire dyld_shared_cache, I found out that a library named H264H8 contained several different references to AppleD5500. One of the interesting call flow was: AppleD5500WrapperH264DecoderDecodeFrame --> AppleD5500DecodeFrameInternal --> IOConnectCallStructMethod ; Calling one of the driver's ; 'exposed usermode API' [3] AppleD5500WrapperH264DecoderDecodeFrame had no xrefs unfortunately, but as (most) of the code isn't written not to be used (or it would be optimized out in that case), I assumed this function might be inside a vtable. Binary search in IDA for the address of AppleD5500WrapperH264DecoderDecodeFrame indeed resulted in something that looked like a vtable. The vtable used in an object's initialization code in a function called AppleD5500WrapperH264DecoderCreateInstance. H264Register was an exported function with no symbols and no xrefs, but the string "H264Register" did appear in VideoToolbox. It appears that VideoToolbox treated H264H8 as a dynamic library and H264Register as the "entry point" (found with dlsym). So to actually trigger usage of the driver without having a send right to the driver, we needed to do the following: +----------------------------------------------------------+ |XPC request to mediaserverd (VTDecompressionSessionCreate)| +----------------------------+-----------------------------+ | v +------------------------------+ |dlopen & dlsym to H264Register| +--------------+---------------+ | v +------------------------------------------+ |AppleD5500WrapperH264DecoderCreateInstance| +--------------------+---------------------+ | v +---------------------+ |Utilizing the vtables| +---------------------+ | v +----------------------------------------+ |AppleD5500WrapperH264DecoderStartSession| +----------------------------------------+ | v +---------------------------------------+ |AppleD5500WrapperH264DecoderDecodeFrame| +---------------------------------------+ | v +-----------------------------+ |AppleD5500DecodeFrameInternal| +-----------------------------+ | v +-------------------------+ |IOConnectCallStructMethod| <- driver entry point +-------------------------+ VTDecompressionSessionDecodeFrame is a documented API that checks if we're a "server" (e.g, mediaserverd) and does a lot of logic assuming access to those drivers. Or it just sends a mach message to mediaserverd if we're not a server. Despite being 'documented', VTDecompressionSessionDecodeFrame had a secret undocumented feature which I discovered during reverse engineering AppleD5500WrapperH264DecoderDecodeFrame (so a much later stage of this API). It is possible to embed some properties in the sampleBuffer, in a dictionary called "tileDecode": tileDecodeDict = CMGetAttachment(sampleBuffer, CFSTR("tileDecode"), 0); if (tileDecodeDict) { cfnum = CFDictionaryGetValue(tileDecodeDict, CFSTR("canvasSurfaceID")); uint32_t surfaceID; CFNumberGetValue(cfnum, surfaceID); ... x = ... CFDictionaryGetValue(..., CFSTR("offsetX")); y = ... CFDictionaryGetValue(..., CFSTR("offsetY")); lastTile = CFDictionaryGetValue(..., CFSTR("lastTile")); } The dictionary had 4 properties (or at least, I saw 4 properties): "canvasSurfaceID", "offsetX", "offsetY", "lastTile". I had no idea what these properties meant, but "canvasSurfaceID" sounded perfect for our case: What if we could supply a surface ID to canvasSurfaceID, and hope that, magically, this surface will be used in AppleD5500 in the behaviour we saw previously? And so it appears - this could indeed influence the behaviour of mediaserverd and make sure it sends our requested surface object to AppleD5500!! This could be verified both by reverse engineering mediaserverd and following the IOConnectCallStructMethod call, and the buffer given to AppleD5500, or simply using the kernel tracing technique to see whether the surfaceID of the object in AppleD5500 matches the surfaceID we sent (which requires prior reverse engineering of IOSurface.kext). It could also be performed by calling the function IOSurfaceRoot has to lookup surface IDs, and see if we get back the value we expect given our specific surfaceID. Most important thing is - to make sure that this indeed influenced the given surfaceID. I personally did it by reverse engineering mediaserverd and following these calls, because I was interested in offsetX and offsetY as well, though this isn't necessary (but proved to be useful, as you'll see soon ;) ). --[ 7 - _The_ bug Back to our main objective, get to that memset with our arbitrary 0x80 write. Looking up the code, I noticed the following: if ( context->tile_decode ) { dest_surf->tile_decode = 1; tile_offset_x = context->tile_offset_x; // [0x1] dest_surf->tile_offset_x = tile_offset_x; tile_offset_y = context->tile_offset_y; // [0x2] dest_surf->tile_offset_y = tile_offset_y; v73 = tile_offset_x + tile_offset_y * dest_surf->surf_props.plane_bytes_per_row[0]; // [0x3] v74 = tile_offset_x + ((dest_surf->surf_props.plane_bytes_per_row[1] * // [0x4] tile_offset_y + 1) >> 1) + dest_surf->surf_props.plane_offset_again?[1]; // [0x5] dest_surf->surf_props.plane_offset[0] = v73 + dest_surf->surf_props.plane_offset_again?[0]; dest_surf->surf_props.plane_offset[1] = v74; } ... if ( !context->field_4E0 && !(context->some_unknown_data->unk & 0x30) ) // [0x6] { surface_buffer_mapping = v85->surf_props.surface_buffer_mapping; if ( surface_buffer_mapping ) memset_stub( (char *)surface_buffer_mapping + (unsigned int)*(_QWORD *)&v85->surf_props.plane_offset[1], 0x80LL, ((dest_surf->surf_props.plane_height[0] >> 1) * (*(_QWORD *)&dest_surf->surf_props.plane_offset[1] >> 0x20))); } The data in [0x1] and [0x2] are completely controlled by the user. These are the offsetX and offsetY which we provided in the dictionary and they were forwarded exactly without any check. It looks like, [0x1] and [0x2] are being used in a calculation that ultimately leads not only to a write of 0x80s with an arbitrary length, but also to control the offset from which the write is done! This makes our primitive much more powerful as we can make our overwrite more accurate. The values mentioned in [0x3], [0x4] and [0x5] are attributes of the IOSurface in question, so they are usually somewhat controllable. In this particular case, the limitations on these attributes pose no restrictions on the impact of the memset's primitive. While these attributes aren't really within the scope of the paper, for the curious reader, you are welcomed to reverse IOSurface::parse_properties to see what IOSurface expects to receive for creation. One problem I noticed with kernel tracing though, is that we never get to the memset because of the following condition: context->some_unknown_data->unk & 0x30 // [0x6] The obvious problem we face here is that there are no sources and these are actual offsets in a struct and unfortunately there's no easy deterministic way to know which object we look at. Looking at the assembly code for this line, it is decompiled from the following: ; X19 = context LDR X8, [X19,#0x448] ; X8 = context->some_unknown_data LDRB W8, [X8,#6] ; W8 = unk AND W8, W8, #0x30 ; unk & 0x30 CBNZ W8, skip_memset ; if (unk & 0x30) goto skip_memset; Because the offsets weren't so common (0x448, 0x6), it is possible to actually grep the entire driver text section and start trying to find the right reference by grepping. Because this happens pretty often when reversing IOKit drivers (or reversing "large" binaries anyway), I highly recommend automating this process. Imagine how good life would be if you could just grep for "STR *, [*, #0x448]". It's not a oneliner in Python, but for the long run this worths it. For this case however, grepping would be enough: $ cat d5500 | grep STR | grep 448 | grep -v SP 0xfffffff006c30448L STR D1, [X19,#0xA90] 0xfffffff006c41448L STRB W13, [X1] 0xfffffff006c44488L STRH W17, [X13,X15,LSL#1] 0xfffffff006c4481cL STR W8, [X19,#0x64C] 0xfffffff006c44890L STRB W9, [X8,#6] 0xfffffff006c448e8L STR W9, [X8,#4] 0xfffffff006c47448L STRB W0, [X19,#0x2A0] 0xfffffff006c495ccL STR X9, [X10,#0x448] ; only option 0xfffffff006c50448L STR W24, [X22,#0x17BC] For brevity, I'll sum this xref looking process for you - it wasn't magical, and I made some tools to speed up the process. Sometimes the offsets are very common and then grepping won't work - for this case sometimes the best way is just manually following the code flow. Going further, I eventually got to this code: LDR X11, [X19,#0x1B0] LDRH W11, [X11,#0x24] LDR X12, [X19,#0x28] LDRH W13, [X12,#6] MOV W14, #0xFFCF AND W13, W13, W14 BFI W13, W11, #4, #2 STRH W13, [X12,#6] ; This is the "unk" we were looking for. I then looked for 0x1B0, which was responsible for this entire calculation, and then I saw the following string: "CH264Decoder::DecodeStream error h264fw_SetPpsAndSps" In the same function, I found another interesting string: "AVC_Decoder::ParseHeader unsupported naluLengthSize" --[ 8 - H.264 in general and in iOS I googled then "AVC nalu" and the first result I got was "Introduction to H.264: (1) NAL Unit" [6]. I figured, it might be easier to understand a little bit more about H.264 (as I had 0 experience with that before this research). The standard of H.264 can be found at [7]. The relevant page for NAL unit is section 7.3.1, "NAL unit syntax". As we can see from the copy, each NAL unit has a type and is being processed according to its type ("nal_unit_type"). From all of the different NAL unit types, there are 3 which are necessary to know: *) SPS (sequence parameter set): General properties for a coded video sequence. An example of a property which is held by SPS is the "level_idc" which is a specified set of constraints that indicate a required decoder performance. *) PPS (picture parameter set): General properties for a coded picture sequence. An example of a property that PPS contains is "deblocking_filter_control_present_flag" - flags related to the deblocking filter - a video filter which helps smoothing edges between macroblocks in the video. Macroblocks are like blocks of pixels (a very rough description, but good enough for our case). *) IDR (Instanteous decoding refresh): This is a standalone frame, a complete picture which doesn't need other pictures to be displayed. IDR is always the first NAL in a video sequence (because it's standalone and other frames depend on it). The question is - how to find the appropriate type in the kernel and the code that processes each NAL unit according to its type? I started searching for NAL unit type strings in the kernel (SPS, IDR, PPS, etc), and found the following piece of code: LDP W9, W8, [X19,#0x18] CBNZ W9, parse_nal_by_type ; [0xA] CMP W8, #5 B.EQ idr_type_and_no_idc_ref parse_nal_by_type SUB W9, W8, #1 ; switch 12 cases CMP W9, #0xB B.HI def_FFFFFFF006C3A2DC ADRP X10, #jpt_FFFFFFF006C3A2DC@PAGE ADD X10, X10, #jpt_FFFFFFF006C3A2DC@PAGEOFF LDRSW X9, [X10,X9,LSL#2] ADD X9, X9, X10 BR X9 ; switch jump idr_type_and_no_idc_ref ADRP X0, #aZeroNal_ref_id@PAGE ; "zero nal_ref_idc with IDR!" ADD X0, X0, #aZeroNal_ref_id@PAGEOFF BL kprintf MOV W0, #0x131 B cleanup As we can see here, "idr_type_and_no_idc_ref" happens "if [X19+0x18] == 0" (at the [0xA] marker) and if [X19+0x1C]. Checking in the manual, we can see that for NAL type == 5, we get indeed an IDR NAL. Based on this findings, we can assume that [X19+0x18] is nal_ref_idc and that [X19+0x1C] is the type of the NALunit! Back to our mysterious offset 0x1B0, I started thinking - perhaps it is either PPS or SPS? The string we found earlier is pretty clear that the function is doing something with them. I then decoded a video with the API and using the kernel tracing technique, I looked at the content of 0x1B0 to see if it looks like something which looks like SPS or PPS. Luckily for us - this was indeed the SPS object! I figured that out because within 0x1B0, All of the values were in fact the values of the SPS object which is described in the standard (section 7.3.2.1.1 [7], I love you too). By slightly changing the SPS of the video I was tracing, I saw that the changes in 0x1B0 were correlated. In fact, most of the SPS object was stored there in the same order as it appears on the manual :) so this was even easier once I found the function in the kext which filled up the object. This was sufficient to understand the mysterious unk & 0x30 check which means (wait for it): If SPS->chroma_format_idc (section 7.3.2.1.1 [7]) == 0, we get to the memset we were waiting for! At this point, I already had some tools to create and manipulate videos. So creating a video with chroma_format_idc == 0 wasn't a big problem. To send a video for decoding, you first have to call the function CMVideoFormatDescriptionCreateFromH264ParameterSets which creates an object that holds information about the SPS and the PPS of the video. This object is given to mediaserverd to create the session. After the session is created, we get a handle representing the session, and give it to *DecodeFrame which ioctls the driver from mediaserverd (see graph above). I created such a video, sent it to decoding, was waiting for it to crash the device and... nothing happened! After a brief reversing of mediaserverd, it appears mediaserverd rejects chroma_format_idc == 0! --[ 9 - mediaserverd didn't read the fucking manual But... mediaserverd only gets the SPS information at the beginning in the function CMVideoFormatDescriptionCreateFromH264ParameterSets which is only being called once. According to the manual (haven't seen a single case in practice though, and I've seen plenty of "Snow Monkey in Japan 5k"s during this research), there could be multiple SPS objects there (section 7.4.1.2.1 in [7]). Which is odd, because if mediaserverd only gets the SPS and PPS information once, and rejects them, then how it is supposed to be aware of the other SPS\PPS packets? (*DecodeFrame just passes the packets to the driver without doing any sanity check). With this in mind, I decided I'd just try creating a video with a normal SPS\PPS properties, then in the middle of the video embed a new IDR, which points to a new PPS, which points to a new PPS with chroma_format_idc == 0, and see if that bypasses the check deployed in mediaserverd. +------------------------+ | SPS | | chroma_format_idc > 0 | |seq_parameter_set_id = 1| +------------------------+ | v +------------------------+ | PPS | |seq_parameter_set_id = 1| |pic_parameter_set_id = 1| +------------------------+ | v +------------------------+ | IDR | |pic_parameter_set_id = 1| +------------------------+ | v +------------------------+ | SPS | | chroma_format_idc = 0 | |seq_parameter_set_id = 2| +------------------------+ | v +------------------------+ | PPS | |seq_parameter_set_id = 2| |pic_parameter_set_id = 2| +------------------------+ | v +------------------------+ | IDR | |pic_parameter_set_id = 2| +------------------------+ The moment the IDR packet with pic_parameter_set_id = 2 was sent, the kernel crashed with the panic we were expecting! Slightly afterwards, iOS 11 was released. And unfortunately - the same PoC code did not crash the kernel... I diffed the driver code but there wasn't any change. What I did notice however, is that the string "canvasSurfaceID" did not appear in the binary of the driver anymore. I did notice that a bunch of undocumented APIs were introduced then, namely VTTileDecompression* (instead of VTDecompression). I was too lazy analyzing the function with IDA (to be fair, IDA and dyld_shared_cache aren't good friends yet), so I decided to go with a different approach: try attaching debugserver to mediaserverd, and change the given values to IOConnectCallStructMethod, hoping that the kernel crashes if I change it to the same values back in iOS 10.x. Attaching the debugger obviously doesn't work out of the box. I assumed both the driver and the process need the run-unsigned-code entitlement, so without even checking why things didn't work, I just injected the entitlement to mediaserverd and to debugserver and tried attaching debugserver again. The entitlements' dictionary is stored in task->bsd_info->p_ucred->cr_label->l_ptr: struct task { /* Synchronization/destruction information */ decl_lck_mtx_data(,lock) /* Task's lock */ _Atomic uint32_t ref_count; /* Number of references to me */ boolean_t active; /* Task has not been terminated */ boolean_t halting; /* Task is being halted */ ... /* Task security and audit tokens */ #ifdef MACH_BSD void *bsd_info; // struct proc ... }; struct proc { LIST_ENTRY(proc) p_list; /* List of all processes. */ pid_t p_pid; /* Process identifier. (static)*/ void * task; /* corresponding task (static)*/ struct proc * p_pptr; /* Pointer to parent process.(LL) */ pid_t p_ppid; /* process's parent pid number */ pid_t p_pgrpid; /* process group id of the process (LL)*/ ... /* substructures: */ kauth_cred_t p_ucred; /* Process owner's identity. (PUCL) */ ... }; struct ucred { TAILQ_ENTRY(ucred) cr_link; u_long cr_ref; /* reference count */ struct posix_cred { /* * The credential hash depends on everything from this point on * (see kauth_cred_get_hashkey) */ uid_t cr_uid; /* effective user id */ uid_t cr_ruid; /* real user id */ uid_t cr_svuid; /* saved user id */ short cr_ngroups; /* number of groups in advisory list */ gid_t cr_groups[NGROUPS]; /* advisory group list */ gid_t cr_rgid; /* real group id */ gid_t cr_svgid; /* saved group id */ uid_t cr_gmuid; /* UID for group membership purposes */ int cr_flags; /* flags on credential */ } cr_posix; struct label *cr_label; /* MAC label - contains the dictionary */ /* * NOTE: If anything else (besides the flags) * added after the label, you must change * kauth_cred_find(). */ struct au_session cr_audit; /* user auditing data */ }; struct label { int l_flags; union { void *l_ptr; long l_long; } l_perpolicy[MAC_MAX_SLOTS]; }; This time it worked! First, I took an iOS 10.x device, triggered the problematic flow, and put a breakpoint just before the IOConnectCallStructMethod function (which is the actual ioctl to the driver). I knew that this works, so I just copied the entire input buffer to the IOConnectCallStructMethod. I then called the corresponding functions (same API, but changed the VT prefix to VTTile) and set a breakpoint again. Once I reached IOConnectCallStructMethod, I simply overwrote the entire input buffer and replaced it with the input buffer I copied from the iOS 10.x device. The kernel crashed! From there, it was easy to reverse engineer backwards from IOConnectCallStructMethod and see that the 6th parameter given to VTTileDecompressionSessionDecodeTile is simply the X and Y offsets shifted so that they fit in a 64 bit integer (each one of the offsets is a 32 bit integer). Apple eventually fixed the bug by checking in the kernel for out of bounds before performing the write. They re-verified the values once again in AppleD5500.kext. If you would like to find the actual code where Apple introduced the checks, you can search up the kernel for the following string as this is now printed when putting bad arguments: "bad IOSurface* in tile offset check" After this string there's a series of checks for the attributes of the IOSurface object. --[ 0xA - Takeaways *) I've just displayed one vulnerability in an attack vector accessible from within the sandbox. Parsing video and making sure there aren't mistakes isn't that easy, and it's all done from within the kernel! It's obvious that there are more vulnerabilities in this driver, and in other codecs in iOS as well. The attack surface is (sometimes) more important than the vulnerabilities, and I think this is a good example because nowadays it is not _that_ common to find simple buffer overflows. *) Manuals are super important. Often when reversing drivers, it is easy to fall for looking for patterns (looking for integer overflows, races, appropriate refcounts, etc). Understanding what we actually reverse and not just looking blindly for patterns was the only reason I thought about putting two SPS in the same packet. I didn't try "bypassing" mediaserverd, I just understood that SPS has an ID, and hence it is likely that there can be more than one of them. Maybe it wasn't the reason I found the vulnerability this time, but that happens as well. *) Infrastructure is super helpful. Sometimes people can get lazy writing tools, but these might be helpful eventually, even if it takes a lot of time writing them (the kernel patching technique was really easy to write, but I did find myself writing a single tool for a few days just to have things easier when researching). It's an investment for the long term, but without the kernel tracing technique I would have probably given up already. I had so many assumptions which were mandatory to verify, and it was very easy thanks to the kernel tracing technique. --[ 0xB - Final words I'm not sure how you readers feel about this paper, but from section 7 till the first crash, it took me about a week. I tried to put as much details as I could into that paper, but unfortunately sometimes you either forget or ignore details. While it was time consuming and some experience IS needed for that, I'm trying to show you that it is not impossible to actually find bugs (good, reachable from the sandbox bugs). I highly encourage you to stop mentally masturbating about iOS bugs and just throw a freakin' kernelcache into IDA and just start reversing. It's much easier than it looks! We're still in the era where someone can drag a kernelcache into IDA and have a good bug within 2 weeks. Remember my words, in 5 years we'll miss these days, where we can completely wrap up such a project within a month. Additionally, I would like to thank Zimperium for letting me doing this research. It is not always easy for a company to simply let a single person to do his own research on the internals of a video decoder driver, hoping that when he says there's something coming up, something actually comes up. P.S. - I did start working on an exploit, and then more important things had priority over it. Hence some of the attached code might be redundant. Sincerely yours, Adam Donenfeld, aka @doadam. --[ 0xC - References [1] https://nvd.nist.gov/vuln/detail/CVE-2018-4109 [2] https://nvd.nist.gov/vuln/detail/CVE-2015-7006 [3] https://developer.apple.com/documentation/iokit [4] https://github.com/kpwn/yalu102 [5] https://xerub.github.io/ios/kpp/2017/04/13/tick-tock.html [6] https://yumichan.net/video-processing/video-compression/ introduction-to-h264-nal-unit/ [7] https://www.itu.int/rec/T-REC-H.264 --[ 0xD - Code begin 664 src.tar.gz M'XL("#8LOUL"`W-R8RYT87(`[%Q[=]LVLL^_TJ=`FG,:*W7T\"O)=;>M(LN- M-K+D*\EI/*9/TWXO'IUC+];KXZ;^N_D\Z1U>'C\ZN3@I-D"NE;S\.#P"3M^ M\@4^<1A9`6-/K.!ZM8ONOO+_HY\0]-\;OA=1XS/K__CWZ+]U"(\>]?]%]3\< M7_`PM*[YP(_$7-A6)'ROOOB3]']R=%2B_U;SX.@@I__CDZ/C)ZSYJ/_/_FF\ MJ+(7K..O-H&X7D1LSZZQUILWKU\>@&I8>[5R.90N5W'$@WW6\^PZ:[LN(^*0 M!3SDP2UWZ@"".#^T+R_[W>EP/.J>=T?=0:<[[?7[$XI`#7L(_O[/Y*H*V$<,&@;G" M\@!Y+:(%-:M0ZM!GGHCO.K"\"#@&SC:`''L.#W1BQ$J:FW%LD6CM@%L1WV<@ M&.Y9,Y=3'7J*G,/C@#LBC`(QB^63^3Y"Q9YKK>>QBQ3PMVS#`6Y7@I-4+4]) MQU_Q`,"\:Q9NPH@OJ:W(I[Z)P(Z7M]R+]MFM\%T3)RF)8D?2*4Z@E0T"@4$M M=[?*0G\>K5&%BE=F70><+P&XGEC0IJ_ M&HWU>EV'AKR0-%^WL.4Z**QAK4*W098#@@0K0I'/_0#%CBREJJYOC9<7K959 M,#I2OE,M@)@+VD7C1!R=R$?;9<_;8]8;/V]P9G^ZP+I=T1`G5_OAQUQV,V'+'>Q66_UX72]N",T:!D0-1] M^Y&=]<:=?KMW,6;M?I^-KSKO$MQ>=TQFTAMT^E=GO<&/:>*@R-K#LX0__5J):XSC=F MKVETFTSYS7>]\60X^BB_-:K59V(.',XK4V`$_K\`4;9_[`Z&D]YYKT.*F+ZK M/I,.C^TD0BB@`R)[Y<8A_JOR.V#+8U]UOF+_JC[C'OA6I/-L-P8C_G9IV8L& M_IA&FQ4/ZXOOM$*'WX+$&O*7B4`&$[WAB$=QX&%9E7OQ$EIB\+DY%T$8$8T> M9DP`AU4J?V.M9G-?$O:&8Y`1-$*>.EP\E/S"BNP%=_+DDKJ5IYZ`$X+Q&QDJ M(/U!2M\.-YZ-*G4Y4ACACXO,%".JE/HDH>Y;Y1)!PC=OJK^=9J4X'.NTJI7> M&9(?'^XG-#K//$OT2A%=6'=$U0ZN0]G:2:$QU?F1,MH>6.8=`]+FOK&\X\

>S9:?62>H9J(SZ'\(`J&LMS+9(%P/C/-WIOF[F*IF9S)%K] MA*(',SJ,[^AWMYZK:&H]1Z*,;"<-M4$JK58;#?8R\\G9E&)N?G)$M8#CU_O% MLK'X)R?]%2J`(]O;$UY48R'0^/,]X4\A9@FF`9\#3QXZC%H-F4&_@5[)0,"V ML/+1R=$OQ:;^`1V"63.VD5X?"^]@[N;!R9'J%;FQ97@]18ZF405_G58JC1<4 M<$(@0:RBST5J<`1Q8+E`5T$.3^EAD9]*RNXI^=?<1>#P%*2)M&0_T8- M/UL%UO728BO+OMD[JB4"2KU1:AJR>BHDK=M+Z24F:>\-&DJ(P&?$&(O]T@(V MH/U,\S6#M9&LGK[OC@;=_O1JW!T='E1+3+#<`!/S@[J:(:5=**CKESSJ/:;S M^0SG/\1L"D93L)E;7SCLQ0/-1''"0+(9RV`4"*6\Y.;?+"])M(&!5>Q&IW^& M=&4G($:_#G_Y!X1/5J2"=*#=VT/FN5.KZ<+6:S1W5]G*7@9ZF?`LL>E=1FIZ M>)JO6:K!L@(#0HGB43>/+X^2+YO]YP(J/YSY+_ MW9'_.SQHM7+YO\-F\_`Q__=7Y_].'O-_C_F_Q_S?8_[O,?]GRO]MDW0P@?8F M\'/R\;([SF3F\B5IP$<%*2%]8RTM+GHJ'\ED8#8S)\/J;-:-2FZ7VX3<[HS< MPY."LHN#JWY?#ZHQ?DZKUI)N$%FEJ4+BI&_X$,)?&1W7FK4$7/VJJ#SJ6&`H MR1PKLICLA2YD8/6BW9%2G.(JK`-V9]DJ8LD2:E37,0BK7J\C+=.(->F$D3/S M?3%XLA5HJINT"6I7>[QED1I)H^DKB+,/#RJ5WG"X0@-\*Z)P M&VN/T])S<<>=4T,U-2F9BGK@*&E=FR]D#$H[8!<<9D]<+C8AS,INVW$@$@@-(`4:!"K%Z7/O.EKLA)$DQ$YJQ/W+ MDZ/I-*U#2W<8"Y9L$5:H($`11''*1+)J2VIDB(W4Z7#0EI6R65I.)@_E"K:6 M`FGE1S#;]_P]*O>PM18]]^RPX/:K4<"&:!*B=' M527%\@Z@,']'![+D#^Z`ZQL9Q/Q6HKO(J;G[3 M:HU@UM/'MEJWI^A9.WD@N*J48)>-LRPA6*QJ1=4CS=!4EM1$IVAFM#AD,YPF MQ2H7I_.:;S.=/_5&U11S8:T@IHO6G'OL63*34>@:+Y]C'.S!6M"3$0.$IW<1 M#)30#@3Y;#D=Y=C_5[4BO(C=6FX,K5<(@7)M[(5G+;ED<0!_.1\D2UA/,M>ODQ"&>`)%C,66_EKB%$PE%W[V<;3K!ZTC6)/0%!7J1%.<=Z= M?KB83MKC]SC&,#JPPIMIA'-PPL=E#.$J!,XH8:R7AE-A%,_GLMEJHY$/)A3Y M-F?PW3:R@4GPW51Z%CV_)2.AR^%H,IZ>=<][@^X9V%(^4LJ79WSZR@\B4+_P MISZMZ*;1:6;Z-4+DION2_4M]1R(!QX9`HYYJJ80"C0>7+7ZP@TA$]Y($_!K7 M`1L`Q)_EA*%,W4TU=8,13(=O_][M3$CKH&RM3HTU,UE633]*NV`M'=E/&"'* M,I?TB[2;VPP\XW,K=J.+E`"W`@V;AHJ.(I!*9KNPYRW$3&Q+MKNR/P4@JI5;0-#"K*BR0G)48?F/#R M0#I&7G@Y>3X((R?FOQ5%_P"0@C[R.GHP)SG-F11JQM&`KF!8R.`Z)/6DVIG/ MYQG"$2SNAYZ[V2JPU40%:B1C7";:BJ`ES6`_`Y'LZ$B2@R+)E2=^C9/R(UDN M#?+%4_8#6=X8IF_.SBT;_$)(57]P1&C'(:6?KC`S`]:(.8T0*:=SHF0K*X#Y M!/P)$YA_^S46@="V,%NH.66XAXDUQ7W(*44.Y$FPKXI@:!4`5`Y5@"+5]#I MNW_6`,6^*8",=W%B8()\J.Y*\A)F6P=7$!J=8&FFI3E!R%*8I34BG3WZ9(BR MM%MYL(1V+P/00.J:LDO*6H+ASX0KHHU<@V>7L7K\.VA),H.0:`XA/:"D`;T#*-*"&LWB=.J9\Y] M:Q'2J#NY&@V,>TIIT:>>\>9!X`=Z]J."FQ[3@-B6F7#9!>V@$HS,J?!O1)0R ME#YA91]H9RI']%[S[O!U30L&MY5Q*R5!C&?RX13&Y1*&A!DQGNTU:X9*<3@K M9P,JM4R5YK",6L._LDH'IDHSU[=OIB&L#M#PBI6.3)7`Z:\6P@[+6CHV5?)X MM/:#&[#J2F5+>F)F*N:1[\.T8\9_;:JT6L8[9?;&5,FR5V*GH(WJ"9>S.-Q5 MRZ@?:V'O;LNH(,JA+BT/U$-9UD*M0SRG6JRW$(Z:@HQM'9EKK6P;EY=E%M2J M50TLPC3I^$"1^+%\M9?&CB6!5UEC+[7&*OI8F@+%GASAM6VUO70<_CL_^/ZM MB'-HTGT1&M38SR!FT?(`S-CO#%]&=G(54M@4]R9Q5^/8MM6N&'TPV3F%F:Z# M,Y+V:338\'VQ>A?=HD96$%_S[F`&83)4O^8>A"PN(T_**D6L@:^2C[NP',*R M+>]YA.&##W$N5PE89H(<<1F\A.60G"`#1 M.G&`DRO4,S(GLP*[T.PFH7D^V+.]8#(5;\2*+@-Q"R'7-1FZ$:M%6*N$3@LU MBX!O+2'@85Y)7T3K^WB>%].).[IZ2&BJBQ17NE2M%(\R MG^5X1SK>FFA+`;MW,-F'XI:WY7`P`AY+S2:DS)*T%#-]Z@<`6<*CY6*O-QC; MEZ@DO9A4VND3XC$$-30";G.!?D^=@@G_()<+T`B$(I3/C1B>]Q=.D+']Q<.P`L'4/:E-.G,:%T:D,T11W=L_11QDNODNL%\!P_-/"MQ0L<<5@ M:0BU-0\C'H[_W7C2%\NQ_P!`VAWF.WR4(]WQ=BNZS#)HK#K"VB4Z1_IC2=!!K.YRX^V`GT2K=\7]4H M-?R=88WS6DWS$9-3@-D\HP@W],K]JO,F/QBMI(H)L+,`?\'=<`=?5A)^H`;M MA-[E\\@$.%Y9]CU"FZ7Q#-%B:H*.,7Z:-W3L'=[P$D9Z]TZ$T:X.2O>%3B&= M?CG5*?.N/Z4+7R,>U[PKK9$=?^VQ/SCSKM0))A7HFD1/TUH0K\JC-6>>B%ZD MM(EYF!#/<>\TW*5,WM2M8R[IN?=KS&,3I`I>)K[?AU"1FR&E3\2T%!Y@E",'%MN>H<7,8C;?<(Q6]&%D\2,?J*J$U(5W2F:2=\CM,Y"?/+]'& M-`Y=\]I(FT[,<-+7+HF0V,.5KQ>98C08=TL+.9>P1KCC6@8MK;(SYL/SLV:X MD\Q*9HE[$08@!_7C[>RF\KOH-6)%;_#@MQD@,]+K+9)_6P(D9QSR0Q4SBG2Z MF"!5CE>$)#"5A`(U8++>W3PU^N#T5F`)NO3`EMPG(4(6P*1%R8'MH88B='M& M^BA!E6Y8;2)@(EU2FSA\"^N;M7"B10F4=,&8IYHEE-#U&$*'F=SJX8X9&*;% M<.5[#B;JS-"%V#)(:QB"\=`'US)TR[HL/;,(?7L1^)X/_&(,@`+D820G(B'/ M-:RL,'I:UL"`KTL:F#^X@7D,>,91'IW[8-.[C';>W!KMV@K5T(1*IJ6.7)CO M@&O5I'<,%Z0QC\,P0+V%G'N?=$HA2;3_)QY3T/?_X6=?S+[P_O_!0;/5*NS_ M-X\?[W_^Y?O_;SYU__]QSS^WY_]%=ZD1YG&C^O_)1O5#-Z>KVCT(\O1HRP&T M-0NL8)/;?4XF/OC9[[W5-Y\+)4;0N%)Z)<3=R;IX"$)-SHG+\%K67K%TC.!.5Q05#W M$(YBK^_[JY(+F)/RMZ7!S_=\8[ZY6?)BU@QE^]82KB6/;EY8>,J4"*;3M]T? M>X/I6;?3'ZO#QPOY!I%DNJ\F?S"Z@(D90%29]U*I"^^,)+L">/9([LA+$_*5 MDZ6CHB_IQ(.\Q-#%.PS[V_>,X)\]=0>"\RL,1 M>"@)'2^L-6X%=+)]V9,.'FQJB2XO&5+U;V?!=]M^!%PM$L,,FS"'Q%&(0#B< M5.]25RW[H%V;V&>&2QGT,+V`L<^^9OH=D3H[3S+^'N4-XC`&+XO'/<#W9MM" M[Y^(;\$WU/_D`*ZKII"7C%_7H0]#HAMQZ6!L\+O)83`BL[R-SCC4\'+=W)?' MLK[YAMFN!7I<"%!%8"\VM-VX#J6#BF=4*H^"<1@^NGY1]RIG(:FA@\DZ`(9Z MX"^50X%HVX5F!,I_G\8]O[/H:B^(0&:D%YJ9*4'*;N3T!>,!U%#7S+9>S=JMW.D&UF>D,(T/M<29H2K( M.E3W(]1?W:A>KH>W M2.31?&L&35C01/)XNZ\&PI_1(EI>F_,U++:B8,]25_<0B&Y.@*#PO7KR%*;\ M+S2%J-5$+:*GB]\$.NV$#Q2MZR-;57DT%TIW_#&QO[Z?(!!E8R M$U\DKU?`@ZOP/-(;!57H)(Z4,<;(4TI%/F8]G$M_ M?D)]RM!F@XL#"V"1!0D,F')R2"L*8]?U65P M+N?<#01ZFR49*0)`>#/'D^C/875IH670@M>B:&9[/UKJ7QXPK4HY:==SC3)+ M?%+J6G2:G$!EGD^9W9\AT/-TNJ3HT?%Q'TL)&+T7[8W1Q5`Y2ZO\LQKY*.N< M`L`I2D^F9)GV2J[O53M(G7$3,]^/L(\KFATO,4J@N[AX89GN[:;.0NDJZV12 M>9')Z2R);8B@JLCW((++RQSH53O[MN]P>6TM4UK5=;*7N7&=X1Q&HTR]ZDI_ M4='XJZ53O*;O?"30H4L,^3F('H8JOR'MP,I.$TI)<[KE@/$H'48C->ET%('A MC"LS[2%Z?Z8BK-VUCI`R"*C8D!Q0(.YW2XUH>)K%M1D=9&Q\7H>,AV[+BZ,;"1_XT M#PG)$4?*W.Z!&4N`K=PL!0;<8','S*;MA%.%]U1F[C\%J8%D.0PY@XSYR&?0 M1Y;2&DYHPB%M&(Q#N*XQ"8PUKG7.3Y=X&AH.2>O)1,SY%%!QYG&35_W0E!I< MF@-<5'"M3PYUVKALP8.!7;+PW.7,UEE;O"(K6N431B`%.ZZO.,O#+BUN\R39 MP7+R]0TO236B=2ZL/H7L[.!=QEIOR5,-G/&3:!`J>K5727KF\($2Z@Q8?=IA M#L-D!B')NT4;T]8JEU6(A/&M-;*USTV^!0_IQM1LD+LND/ MQ#G2L4D:2QW<)_8(G1'7D,"@LFR`S/=3[K6X!*;@QHXBH\)#@X_,IT@ZIW1G M-*RS,RS2U#^#.#+WY&3$9IUS1A.89!^K^@MH#; M.-&;"#2!):24<'&C26["SP/$JP_\5A';DB#2&G55`UP)ILE5T@+W:`,`?21A)2.=F5BE:EX"$I!.5Y>F63@U)X&GDUQ%!_ M0O=]3*Z:@KIJBG92@.!"2N,#]R\Q@$V*BKL+B,.0A]:.*;# M<@O]&>>]5]$U*SF]44R1&")>6D';G*'B;07OV[B&,$B!%^C)<[]^\..*]+L@ MI+&ZU34]D%F%=^7BE*(:2#E9R8VT?'=&B9\8C21R'V2+PYH$Z1KO/N1,@#`SO)>C86=VZB.S4`C:1"Q`)4H$_1=K\,L(TRV"0$*T2A&5"`;S9L M#>?@(F7?`>)XP"1D.EK%6@FH.U>+=,;0(WND1LK6L&(M8O.HBT,ZQ[L/:AKL MF^ZVHI7\Y1Q&?#F1RZ17J+1-,LA49U!,I2B#)U""ED]':SEJZ&S!JE4$I'NR M&>A5;X0&Z6#D_$Z#R'P++AQ\CVPJ%2:AI35=UH13?L6`&DI]AY`W7UMK_-QH'S<.CEL0+QFR?_Q5IF'9W>GO]QNGA_WC1J]UGJ'/O*7M MSF=!C`MX%,4+UCF190UZ&C&DGA(2/`3Y:J/0"5HM/?[$&CTGY1M(LVJ$2/'* MF[9R-5+4/0IHER=*Q.D>9@2FRWXX3VA!B1YD.!B5SP/=,2LJWMT[](?)US,@;P" M@:^+.`IC\H0!:#.2J\C$R`X&[!3%1I6"1+% M1`S#)8?"Y-+5+!A).<^3QF6U<80$=$3]!5VX3FXYM-+(Y4\[HA`AM*.DPJ4+ M"*/CF*0+G8H/FK?'&;FZ!7*Q?"BG5WBX^0L.B..;GS'FAF&_F@:L=/C(#$9%=+7/,9(#':765LZ/]4?HMEA>5PHQRTRG2;F MN')6$W2\QK:%"RM43VM%ZY!S4%VP5[TJL5>QWB!$\N(UIX.)-DNA1A9+,XLC MU/A\`4200^&7,MI0,FMU'\@@L``M75>+"M;BW"LBK(SP/9*VP(L;[9(*$+N& MIU]E-SN+0PPIB9EY.EUO=V?KU1,CN2WV+,8<,LFY%X6V,.RO6S;<18IN4>1IK5@-D?N MIVY#8(DB\@EA@A-7K#(6MN"(`W+'$(*$''-Y?`$!6K2RZ'!&ZOP0L?$RG(;) ME<`X#9_6S*6KVG;(63-DQZ)#_O/LJK-GB;$T'MXB<\$;72BF2MXW95%_ZEHE M7%M^4A%SQ/PN(&.F/I@.?$*O1G-D))3BA:T!]:+AE,4$_1+6Z/1VD&PG/\-$ MW7*_!10*H*.CKP;#;C11*)D0!"[`1`PB'W"7ZU#9?X,,C._$_0HZ`=M,"J*>D45%D`Z<$4:$@W",0'@S@[WR39!JXZB38;9&X+YA9V`M M$-V$"W0"DP/966,X%$NCW^@CR3`,C80E*A[;0QYC!:-K3(?"72-C'4E&L+(/ M;NXJUTMO1@ASJ5Y`+2C*?)_@BW'O:_A9"?>0BP#>@CDJ.#'/'HWIJQ@!GQR8 M#>HSY'X6\,H%H3I,D92\T330+F+APF]\[=\D:)@_G[A,':ML3359&\;9"T=X M>"%G0M+T7Z-&+9G`6@*+AVO>*RX7ZVN!$0)X,'HI1(27>BO\GAZB,`63&?)_ MU.C$9$)C-8F8R'5$@'&H_4)_LBPN@#OH)5XCH@?J``+@L5;),.$J&,_8M#3/ M.`C(@[=PEE>5YG@(:C7]\4'WT'X$4>J.PG@"3O)G?GHEWEF\'VR'PCA))2X2 MEQKJ_!WP-J'(M$0/LNP\20MTYJPG+H1)CYFU`LYJV4$KQ*^O571/9K#\DT!V MR1BY/20YS+N`=2<:[W[BW$^<^XES/W'N+\NY:6FE&5-CZA+:':R[;IU+BL[2 MF4.[/%;?R5HFGS'?AO?;KL1,ZI=00:U13O>,_>ALIZAK^TY+9SG37/.VR=G;>:C5[KL-_H]<[;!Q]ZK?R]+X?% MWL?^5R]BRR+>6H8]:\VN<@/EK=Z0G3N$)L*@!9A0$+FPB&!AC%)<'B?""%KZ MN^F4@9L1N0CSNWX=XL\-2,FM11<4/&.WH[@=J'`0-_](BX!J!%,,;Z3"1&FQ M5K[\YD:N%[BY-7*L^/6S+VPX<31.R.Y5V*(+90U>0DK&X\>PKYAQ"8`#Q:!I M^,7I@1\FRO>%U)`F.I,$D"XXL:ZJM2 MJFLZ\@R_!,Z?V(YD3?2Y4PLMT^#J-^WD%#=/\EGI_8G/-LLGKZ0A^3Z+&RZ[ M$&U]E@A.%PR(*,JS,Z%[:^,Z89-JBKM`(1/IMDUUY]CX9H1TX'J/L>O5T$AA MQ(_^*SD1Y(BK"QP_+*MD*LW[ MQ/B+$6OH[5OABMXS:4,<%Y@$UH1;S;Q)5A MW=H8I,//1J4T_\;=X@R3"J0W/;`0LUCW":E.7'R7T+#TE8*!XHEYPE?*$4=/ MABLZ&H'073UWB2@1LDYY..HJF[L)[?\28JVXU.]EH+HZ=_!<3*NZ"4C2CWAK M23]>5OB1QAL8#XNF5'RU!GM%-\T&PM*-X"Y$F9QHBQJ"GX":CM4//X5<*-"P M#[9['SSS>2H'KG/AT4[Y"5!."JW?^/F2\SW6&CLRCV(TN37EH[KW/*:I`QDS:B"UEY'-U=22+)B+_)(P/%SO2MJW8P12_=OEOYDXL5;9-<;R`CR?H7$28_5)!C4X1[1*[9`[O/6%2`J7*QK72<2F/&A)5F<#CY MOF<9`=%5W!Q+SJF8T1F@]&X^1(;0:4P%*_^*'Z7_-PXQY'SQ'WN.;REX# M+!>!'A]%0^L#R.5G>*K%8?Q0R%P>&5EIJJT,J#$PCHF@Q?"?1&*S&"*_HR,^)):K9Y+` M(5'::6X+%UJNAV.5]76F4.JL%U#BX]%+SMBUVFS#N17S8@<`[6[YB7U]RB6]/2 MK)>UN9CM6M!_?E?.:[2&M&DS7VNJCVR]OSJ&KJ^RA3:>9R#.DKR<+R+(Z1;V M-.3%,R1^0-4+YJ[!=`<\A%JQ/-I`K.#7+3RL'UR5B':-UA@20LH;EZ8%!E>M MB6PE\I`BN"9YD1HX^?;M-+@&IZHF#R9H1/TA3_8YN',^8Q5GE#DCI(!AV3P& M%/T?`W^K^-UU`\\T5BW2:/B&'XHKL>!21T9,?6N!ORY33$BRR<#.82<"6)=G MR`&Z+,ME^QH.GST(J=03,9DQP!)?1:%H4B,SJJ>DF),Z'2VTM9X,PT2K"QD4 MK.Z1BF_&`(E:',BG42HL.V\1+R>T+!V+=$-:Y$L9_:_=:5)U2#&!`/+:ND>- MU`6)4+!\8,TQD_T9W\K$P,5($L@[1`Q";OWG1M]"&0^)HTB(A\42O$(MIAYM M/27]O@$FMN\("!:PC7/"@3.X2BUB'VSN:)B(214Q;V"@JT!OPSJ@)>3`J&P> MXU4C=H(;WG`(ZPK/)A$$X^1QH&7H`AP')VD(X.//TP@T*@,D)]!;#0+2@VIHC48W$'H:/7TE]F^8SP6'DX7J.`3"!] M&#XS(\43:'%G(B(KP]/EZ7\I0HLU#"ZD,QW52QV:*V(:]^^?H[%/M\.ESI!L M=S%X5KBQ(P9R'J>S>M,1RB!>K3$C2G9F6]=]O&`@VIV#UH815M#FT.;LG'M` M+S-HT^A:&Q"R=G%7I1B[;?!@7"+8A@KN)#PK4-S0AF)RZD(VS?G?&X&KDK0,T]N"-DS[HQ&-Z4Q0>-X5@QKN^7`646 M)6=%Z_;.N"%N4HK6NAND]BVGM>A=<-V5*5A$&$HN+,GDF@NBTZO4-R+7(+_Q M`PM5C'RF1Y@.X+X/#=%HQQ&;#EO6XGZ]5>()'I!RC:ZY]`+G)-B$=%%)>CS! M1B6E2C3W,#J8"36,`"YA%=K238=.D-J1R-$>TDC:R.5.A"IE,5*EL78@['"4 M;@EO]]TAUU>"NPXDRT=B^VRD*UQFN2X(13A^XL].&`./;S)7VS.&$/$0$_%" MIACV`PWUH2RL:*3!"`SDF5"\'&I+WHO;M4?23<]%VB*&1C&(`&@2T4#Z$^]@<8\QFK:&:TO$;6%7)E M9$7@1;.57WA246NXG.IA<,N2EPY+@QJN;A(,EY._W@(6MI%3)!4PJ'LQDD/@ M$\@P*@B7+SC9-F![/`"#2'$17B+"R@39V`*O?B7U<`5:'1]$*[Q*P8`+V^S\ MJF''ICAEHZIVUIC>7*/=+AV(A#%D$G`E&ET/*)U?IDAFA/(`X(4Q:-$$QC2$`5V-,/+ M+E@:HSO]OM][W__5Z!;E;&8SNEIJ]GKGS3. M3EHGG?-?,1Q4+9<52EY4@@$J2K%T20(K(3_?I,_A">D@)'+0&P`-/"9HU<&D MJ8.(=I%GG:1IUUDV5W$?.OB+%\#=\WA MCQ^FDQPI6ZIN!%@Q?A4_)]LP6(XQQG;3+L9X3UR'3,.1YNE\R`ZLX115]8H% MA=/\.=Z.-?`I/PAK*"`SXJU<+?;%A"D-Z>XB3H'!@TN<4I!8+"&M?BA9\C7' M6/0TPY3$'6#,#O8B!$<]6"R#+)I).0XG1(YQ2D$X#;?#ID'"2- M(DWHWP3P)63;JJ)6O34&46>_&G'LXY=)0@81ZS29112,H=/5YAZD@RT9GJ$D/5I9=P5F+-+*ZD21 MOVTUCX`PP?E)6\KB.P79\(WS-B&?VFY616NRO6+BNI%Q4;Y>RKIQYNR0$[22 MRCCJ9^GRB:YRZ$K/Z5J"KO*%0>7<))/D5/3U=-'=S;T8M&Q[S6AR@;9%($5Z MS[SV\=F+/:_U.05#*'!15@9Q;]\&['',I*,3)+I<*(`/(Q6QC685)=8K%=9[ M>ZI)QHR8P(P]BM4[\J*$$DS$98=85`*PU]FZ^*8Y3>VZZ#2)'ZK<14<]54J< M@#WM+3:C]4!]1_.4.F=O.O-4ZYR_TGK?Q@*4E)>_U3O&M^KLK;UG;=2T)HI3 M#[PT4@\4+`;FAEW-BAB5(308_C#K$KQ4B#!'P_*E8\GNNMR5?^.5IK9727QW M@.CCPO]5@N8!J.!?`>Y==H3WXR^_%2Q'VK7*?6'@"L'Q:/GP`\%ZQ9)0SM+U M8G^V4\TY&W$I3FDR0B8R?48YK:"YW>6:,U^0YZOHQ/ET;T'/+^ZO M9^?3%PO&L__0XW$^W5\PRI>/8Y3.IR\7C/W58QZ[\^FK>_$-R+,!I\.3I0MI M8S`1T^P5W'49IV=/AK=6@H2B7:[$6-@\CX&;>#$[7K,*8&RY4D4%=R,2J1]7 M;`9]&^677)-":UKM,[Y2A.W`XEHAQD'XNBE">"<25,]J4M M<\2S"_(>P)<\!Z4X%G&/>?2W%>.1S6B>Z+))<`DMD+O+)M;W.]6\\WI:N,<=$L4$RB,,+ND+1,MQ!51YK2J6J,Z(, M7T8\_^BC<8Z62&XNF965@"`JPJ'6Y=U-@?&Y:8.#?L],L'K?/^N<]_J8Z!G@ MP6`WCP.9=4PL4#^`7I%N7&B7ZT7OU`X;,SEB<(2H1(Z0WHF>D`CT=CAS-HT9 M*[\HMA1;2H`7^=3[%<+&DN#E@%C913JNKS60-WJD$+-JM19-%Y`*9@` MNC_/8V2:PVB`@=W$7:0!SJVKQ^7ECY!MR%S1^M*5Q'2V$N!@[H/3ZGRJ.;%! M4U\$\PV\M5`_)^X^S1[$#8`'$L6VM*8A)B1:MQ..U(LSDF1>=P,_'EQIBN?: M6C"=3[S?$.H?56EJ(3@/!O,X"3^Q_;[R@[?S>8<^N_6<\F>0506L@;3">VN_ M%]"Y.6#;]Y';-&KQ:X&W`3&DRVY6)#\DY#EI#AN"SV8SC$/"NR`%7$:\$0L" MHA--EE%Y.!ZR"6?V.63`B!LR,FB,D`PL1V8T_.3 M/DX@LQ8EUYY2"ZZ6B.S)M5E,E?M1?0R"&>1*1V.U2SD(E6T'61(?]I!&.HN# M3V$T3\9X8>:CQIP1_.RQA8LASJS?=0&S%7DSY#)A,=:MY.-)03J"NA>,MF!T M7/;$6DX/Z4(R$I(.)K`L1@MP=:(=PD]XYD\IPW+X&:&1"1EXHH.,]&2Z/:GT MB&>[G2DL'G)-`"(DV%Z-W>@'%ZW1`83$@LM0W:^.HXPQH6Y/`NX MZS+,BIW`V/)/+7GE"W*L&(0@C)Z"I[U_;XY%5"8/(_03T7^4R>[K/[&:Q:PF M5S*PQB3PDD0VB8]J/'5@.X+=(_L1&*RRV.9FAGVTK,V5/`[YDBU85@@(COCO M3AZ7R^2X:BO#Z^Y51Y&7>5SQSR+-198K9/07A,_*>88GE2=$#IW(F:\FXTQW MXG_DL0&-YNI>R-94^OU3%[I&R220NM*!0*AZ#`!.,:L8!(@0[^PVFP^HY;)5 M9]KY'#KU1?`<5(G9>=0Q1W,4RU!3.N2*P%J?06F2%PF(DQ M3B![QIK3;'9GW:LDQ531>2$%[7!PX`3C@@D>2F%VH*"2E4ZC0XHZ1.GL8\&L MQ6A]'/AE-JE\21002_=%DE$;W,TF>CIT%Y,\5OPI2,&R*2>HA*^T!;C?@O_Z M:)I1&>9@WKFEX!&IQZ&I(5$J)FP8S,=^++2+#&BC<70!NZ6=)UT/QHE=/D]X M66R2Q!CM@=C)I#&OJ,2HP>>YR;!<>"DV((I=`QE/?#MFB":UF%HP$6$#0QMC M>^K\9(;G%R:!TN_P8@[FY'H,AY2+-BO<^?@2+]CRM(CXP@P.Q[M02N?-MZJ$&B*#WI!+RY#43Q:D]*T8_8YK:D@S2,G MI!&[3Q%ZS"DY*$+*#%63)V!,;///*2EO`]!S5D:RS0HD7S=]R6F7H#$+1*LE M-PO?5TAR?,P(!5*(L6Z!9BB8S)FV#"@$FCSJ$2%$_,).+(+T.>/A"/)R)V)T6 M6,'?W1^'/JDK,&H\$H2\P-8T939#P`.!N&:^!:GB7!X5J?)K.KC3%`3*T`:0 M.R=X`Z=,-Y(3P.!&3<0/D+B)*?@0PY3CAN+I,C(LCH`79Y@W@A9$(EMN!B#O M`,OQ`'Y7MPSABX]&__(>3[O(6T#UQN_V8=YFRZH>2MRSD$=H?OEV`GLO@R\X M1R9N]D#56(.F^F+!T(0L.^?V&3R?\TV6R?B#.`+_*CD*C/1J%.%*0'G?#2,` MY4+`^X+\C\#\R,K#E>&97^5:@Q01;JNUG#C1X'TDFY/)E(@YL7X9&.F"'?77 M%$M?LU=AT[A@7".Y!56SQG-H6LR96@#"P:S?AZO=Y2Q`+4!V89D+.<+$^$I> MCA1XT,K]S,R@)1WF?&5\9H+R>:(YZFEL-3]WF+HY(3,8]O\@FB=>,O5GR159 M)BWLI:XCH>%XZD_]<32:!Z;_N0C;`SE&O>!3$-_(F)72[,B\M4&ACL^>>P`: M/G;O-/(9,9(\U:0LR?\@`6+=;I+-4"2,.:\K#FL.B/0-?PJ':)%5G7 M&(?VJY`UQ&#-<.$7AO&1"&%#?7+#;WXY/@Q$5I1$M01L;NC'PT6XQL[`,(8[ MV^)HZBY(T0C!/,5C88OS"'?+C*'S+L5J8('AFY!BB"$5QKS$3@&) M%ZQY.677;4%BQATAA==\D/;#Z05;65:$5'-Z"#MA=H2!,TN($7<+P(4JZ5(6 MEG<)PN7+-/:6B=$6#!&Y2_&8A.Z2B!O8A$P#5>?&=VC!PA.!`Z/FH67SHC+Q MO.$J5Y\9+(F''BH;+XQ'KRFUG1B"/P<0Z^XIM-=C#D%41'0+CQ4BVI"W*,Y7 M;B_+1_NZ%\)&:5P_LCXVPKZY!5GG12J[-4W?%%#T4TBQ1T[/"_9X5W2Q_/!B MGAU@K/AJJPG"88ZKA':QI8S\/Y%/9.8H2<<]<3:B:V>W\4B.\X3F/RS3T?)C MGK@D]DMUZ[9>*45-TFQ=$[1AQH_@R"7!@SNK;3&/RV(8WIMG`EMVEQ6%"8#R MOA2)`]7Y-U0&]JN][C*0[[:ROVE!FV-!7X((\IWO$TU!0?CFTEL.:ZMEEOS1E6*/P=JHDN:P8;@N><3$CE+J@,V46G6YE?LSQZ/Z"G+U2NRZ4$,^T#H\U@J MHBE(!H8<`C8VMD]BTUO9H1.?'BPU8BU&&6T2V#B9L;*NA&N<"#%$LP+W?2FO M,+9E."CE.*=(;YPZJ5L(043-+:^IX,7=$#C3O)R//7VAGJL+5.ZU%^,\Q%!- M"J&X'CFX@=HNS8%<<6H$P#^#.#*"ZD!C0],*3&B%53PP.5#&=\7*\"+2>$/@B=@9U@\F,90EUN8AA"F!I("C@ M41A/KFD/P';2.`AH*#S]N@8(&`Z/9"#O5CM'Z$[`,`BM%XC`(*7A6%,!NTG2 MA+6;+)]HZ;:TI.-6:7HZZ![>!RUA>44)K!>!;AK#UNA+0UIL5F4*4543LOOP MT9LN&H2(VO!:^!,[S9_RAXYK10/56;U!5[+YQQ7$:4E;/:ITD0SEI828ET&# MA"M/%+@\!5I45!202=[_JW@D!J6*57(1J\Z^P2_GGJD67KIV#'<8R2;C_'YH MD')5(F3=NQA'@X]X713`.DP#=A0%-S-.VS6GXPOW[[/&3!9[TH60'SSR]K:Z M86=HAS[AJ\R(DJ)/]%@-.BQ)#_#5L!4M"*-@+@M@^U4S&URW7LZR4`3*)\ZS M`LZ3QQ+NPH+*N>@IE[)[8C\9]:%T_%IL]%R*/$4'$$*6H2,K]TF74:VPJ:X@ M=3`DTW$PS[=X*8="1K[Y;G!6_]R;#P*&@)"LKSD]22<)A^?O<:\5Y$ M;S\"8NY%7+"P*.Q#&HY#,C\1C-P@)-P/X/19P'R5/3<4M`1TEZ/VO]M6/Y:1 MM'70SYYO;RBVW\J4.'O6\&`PR!,.=;BC48E`>U%'*0-089 M`1-T#ZENV*"I5?'%!HV#PH0CGZ1FI5R4`]K=E8,V8AS*A&TYT`JCS^@R#2@E M[%Q=&4[1E`]^7=R`Q,2:GR:,QI@H]$761?=7B4<[E98QX<7+MKM@V>I4;P,: M7[2$>RM?PO3Z*R&/!I7VEE@E[?ONHA5;^8*-(QYM]^M;M*2"AKR. MT1LZ^?FDP4+1=]?++[>J<+ M8>&THP3DESAAIQA_%(@S\"09]:\"?QC$/#C-AL<>F;XX(JU69CCL(VUF`!YF M$KO!=`C6 MWE4Y4$T^,M;+)2KI0-,_EV-_I$II,I_^V1`N4HM:DX#*G44/C3&8F.VL/SR3E8Z\0>=[I8_F^7;7QV&R2Q*@A;CHKK5 M8"!_HSI#_/!JWF'K[+S5;/1:A_U&KW?>/OC0:SG6Y,2?G023*,[/QZZXZ00+ M]I!95B`L@"`\V/AZ[#>PQTG?'P[93I%@*"4?\LG!+WI%K!$QM!)==MDOO7U8 MPR1_Z!Q8S9^ZKN1U=',=CM,;VOHL16EV.C`$7`>^51;UVXO]88C:I;&1])W$ M:SS`Q]$\!4.QO+QZE-B6DMRVZ9].U$A8\,%NV'@\5C@X0$P:H10S:,><&-N>:@!E&+692$7TC7*M.# MLY+9]]%0@`#R:!@!V-`[YR> MMIJ]_FFG_V+OH'_2ZKWO'&8XJP;VB3_K3YQ;A6NOR-\L\G:+2F7#N5M`+G:Q M53B##)3TUJB(N@49ZR^N_8\!?J=A4Y-Q<-E/#75$ MSJAE$3[OKX\_N&%8"F.^&C!^,50LQ72^5C#>*S_8.W\Q``F$.,0L_[@Q^/9D7E%KW_2C_DA=>,9C=Q.+I*O>J@ MYNU^]]V;S3VV-%YC-AL'>.B=IQ"RICT=;'D-<"J&PF#WE@3Q)S1:Q7;^W#@[ M8T?N3O>\==0Z;YTV6_WC=K-UVFWUW[<:A^RTW>TUSGM_AK+P']D*A6,9<"OQ M.N(VKPE!:AA#W`:5;3245U-XHVB4@I;\1/?&I*P;D!4W#L#!##7!_+J0YG0V MOQB'`Z\;S6-V6C\.!\$TP89^#BB[WM[6CE>%\L_YR^>U+>_7:(YF5Q"#:8[Y M>,3X@\^#8`:,'=H`SYYQZ$^%?Q$TPULA$T`.OE'LHXT5&]D-:QG##NF%H2W1 MG989@2P/T<`PF%(4=.&GCQ%O8[8N0]!CAQ=S'@.W#DW-IV/_&MWR8OA.?0S) M6"CAOJ\$'7XK"O$Q;Y(TF&!?:81S"^/!?/()KN&\3V$T=HU$%N'#H7)\).#T M!PV!KV]QKUX27:9H)L7'ZOFC.`C@8G5+8-`9F2J201>:9\QNQ/WNL:@%60&] MJS2=O=W>OKZ^W@);HP17'JX!QL$66[!M?Y:,M_E5M`_A#AC(+R&8E@C'RI=Z M2R%OD,56S$HAQRU7(1AF5Q>0$]K1"^%EN/>\T?7:W>=@Q!LF=>^7-MNR/_2\ M7QKGYXW3WJ_@/-8X_=7[2_OTL.ZUV-O6.334^BN3!;I=KW/NM4_.CMLM]K9Q M>N@A47JL4.O@5^^PW6T>-]HG7:]Q?.QU/S3?BW;;K2ZB2?NT>?SAL'WZD^SX MN'W2[C5Z[69=^WN[W.^:_T MB\EIH&&( MN46W#6/^F%JRW>?MY>)OT5?<`AY5/P^('R88U*-UUXH;A8V'1G%\$@P%HL'=$E1:=[[AU9UP#(:B,^V1618?,O81C*+X M1I0V'EH4CU;BF5KK[E<9=.9&YDSZBQEUX$4X#_8`F\,X\H6GO#812]^<& M2EF/J5<(CLX$3%9UB%;Q,PR_84P='G%NP.=-3TP0::U#KJ[6=,C$84?7ZN7Z MPNI-Z2_/H;>@5$Z+W2N&<<,V9G,8!-DQF>_7`2Q#N56)')H\>:%,%RZCOOO) MQ[R)!&`KXH(!OK"7?:H[]H#^Q^02<'Q)KG23/9VM\M?V7I&DKGJJFE[$15UY MW4DJL;O#%T6=90E96IT-<^K)`C;$\/(U2-(%H/N)8N>V>6F6:FP`59&^I%9R/!@@09CRDZ%ES$Z7J<4) M/X>3^>0`O+1P4SEGIT8!:D\-S5ENW2AECK&HCU\@7(K5R7I>N?6E^A`6&:YI MZ'WHY>A[<`@2$H8,PE9VLERBW M?BL^<X*/WCPXS?X0Z&P#^:0T:Q-Z4W!:$]P77^Q7M"2ABJH>,2LSX@>9H)KGN'U>0)( M%/ICX3N&#IPD5;IV\BX6)KSD8J_KU7K%`M72H_KPH7VX:#!0QAH$/')USL#7 M._WYO'&BAL$.%\8.BJ\/`S#V%@=1WCB^V3QL';=ZKK M-^N5"IX)!JGWOYXOO(3@=^)N"H[..ELUWR#T()?0YC28,T%C+!.PN5OCTH^C M-11]H#7!R*1CH@F(3E?>.:++9N(@N6K<30R'\S3<)QLH:9-=M.,X@!;WV[^?!9^#L8' M%)6`=2(YT&GCF"Z*^\W.8:O?;?]WJZ(^U7VVQ9#[&]@,O^FGWJ@?#N/^9_7V+)G1?8`M:]].O0V?`[U<2;5_@7G[S"HOL#[-['O]C0_@LUH!VJE1DN M=6,]R2+ZEC'(3O[-[N;?>-FCD]H98& MVA-?G\X>_'WQ2FN5`*655V^Q-QSQ[D"K06NAS_!2>TOE]7&_*0=.[.U%*:"O MX,E3;T^]W6=O>_SO&COZ9;CC)W\<#OO)+#%YY/:&BTL"+6ULL[9>(^_8UR@5 MO_M(FR]?*2I[J='I!?*:5X'B0?SO2YK=_AOJPSG."]\QRKPQYH\PN+\1SLK! M$%I$&-)N@GV]&!#O=+:;!.'B6>_13O1*S66/MI?7:K[[.+LA06"@-A_:<%X@ MY_R.H(3?!WS6N[MJC$-MB_+?+(F-R/'?7*K^"09J_X3>!K0A#K0]=4_;4VC$ M%VI_>4-0@Y.1G\Z3M9][O7`<'`9PI@-E#CN1=.D?BIM77:M4FB?]TP_'Z`1F MY5(MCJ&@YUG%1CJGT([7/$&YY0@/3(?*?1V3FK'/)^?;>F8D1N@T(=1@Y4.2 MJKK?L M53P$,Q5M')%1A(_RO-7[<'[:A7\;[=/68?^LP4[I[!1_;M2U>N,K![.A/CC` MAXY";&2U%;3 MLI*QT'.%W0O.#L+.\#OD+^! M%0-G_KYTR81H85'RN;J[L_6F5O?8**MOMG;8M_03^\J>[M4P;HE89,\X<_3) M_+%O8$>?+UUU!40ODQ;>G?3UIF[/`$JTF%$T@!C/%)D>[@`>M@,G.^/9SSU'(U)E1_,XO]T<3W M:#;5W,G4,D4STW(4P=EEG\NY95]I4W.\=,VQN)B8;&T-5I5;*!^)J&UPR_?6 MJSAW4WI(EP%]+383-*#QQ;=,U)#1H;&.1W70@)JGL6.%*G+-T2;>*.KIH9]\ MC%Y-/-+#H&I;9-9,*+E`!L@;=;5@PV;CS*L&5%M94V-GU,L.8SM,%JF8Y[4- MOE/B@)._[?$S6R5STJ[C0WEH7*O\KAKCX7G@'ZT)^!E=5C,MU>K;V[GOO$V7 MSK2N-S=S-C1;T`0?,`$B#ZB$$1`R[OW>J_TS@`ED:/HAE18JWRFY2N[:VB/<5E!_DEMS[$ MP>7AO/O-HS[-J+#X[_:0Q/S4*M"@"IK-5L&&EV1!`F+BPKBOW`&*N)"\7]:\ M!X1WB%@-L0+VVH(C[R837J`\C]O.?<>,,.V+F!0TL^R4JK8$OZ%9G'!&!.U* M'E2Q*_A,R$S8.R"Q=X*I<(D&[GI)D^1]-&0G_$H4##9Z<)'\NUV9H=F;LJ"9-$]&6OQH<+`L8"2`J(#].,D??L@;-">!UOEYY[Q_ MW/FINMZB.-G"[E?T2PV(C0X:(&BN<\J$J6_NPO=1!)Y\Q#TX&7*X[P#<,2KH M1BUG/``:L?PZ6IC`8#A0YXM9]W;K'CV&O_:TL:V%DX2<4>)@HY%'J;EEN4<> MJ&%N&YKQ%1\=Z$RIQ;?ZGN#]$3$.6,UMF$T18YD&UUQN6<1&+&''L0?D@_GUGL@ZCF-9$'G]# M^:N,3@+B%!^PG_T8?P-;,Y]L#?/;8!T9)Y]RM>DLH,-!HRIS4]?HZC22,LL6*^&;&B1+>GWK_6FX7O>J ML+=!:WE;#>WT:3"9]:]@E-?I)8/'!FT[&[5J%6+B;M1L@O^6$70#0K]75%&S M$2C1W*D)NK6)?8EM.\-.[%W+*%!^B^;5/*Q6BO!^[G4%*TR%J6+5`DV===#M MG5?7N<[P`[5>MT;YSKD79LODR]9LH%D MGIG1)1OUA3V9987],^4)WMAOR&X\F[%_V,-URF&.8>'778<5:E,TR79M;P+A M!S0336]&SO`8H(#,=Z=S?URGD`^?@CB!'%JCGC%)*O<*5S45BA]#W M>4+3R-49]!.C2<)5KXS_*Q23(63,P@P:ETF0NH47Y(U"Y8 M$^"?C\T3[;39?['7./_I8'$7DC?"C]^DIKU`(L'3'ZWD.F\$/B90X(D&%QW) M%5PH3CB^DK*'(#6#QM2IKN[M6._8[VI:84,XRR'\I*N.NJ$4N5:*TSHIJ^YQ,X''WD:OI< M9?G\V=9*^D,#YKDJ0VME*K]3<@UJPT#5O"8L?&:#@"9NL^-?@Q];/XHNLIL^ MNK@A+T,FB,&Y9AHQ%A`-O6L?_>L_ M3J-K\,R"U]YU0*K'8!@,O>H-P\UBU8`<]8+M66VR_32B6G7/L1T7;\%:*S>@ MH&&O7KQ^L?_ZY?[N:SS7'T01X!5L.V,_87V13]I'^>;('R>HL.+R@KRU4%HN MR_#OG2PKK;KULO+ANSSR,VBOKGHD(04O/HP.:W5#/+&@)G;*S=V][W9WJY$O,V59T?-'?HB'>[=6 M4"@$G_Q[EO?_N7I@_Y\=)J'M9_Q_7NT\^?\\C/_/!!.J%+O_J!AC_9_;AZT. MA(7M0#"T]WT58RS["CY67I[,GA7@I6B1JCHC!RZCHF:PB,5'CB0O^>_-_[J9W;WTLX/_LQVN+_^^^>O'D__D@ M'YVU?^BUC[LF2Y>/Y+/L@8S]4U-9X_'G.[B-X,HI9+4&"Q*-/K&>QT'_)_[' M`**`?"'ZW]U_9-;HO3=J,DEF MDS(EL!JSF(DGFY#KOK;&^\7"JFYM6\052;;#,^BJT]V:\4?;A\&G8`RW;]NL MIBKP'U5K$K4M-M2USL%_=EGS5X'/)+%9[-]L19I]-.UYD0<^ZY$I>6U%:P2` MXYMA6EMC7]ZR`C`L=G"&QEBARVFT"?%6!NFF/PY]5#!M_L(>\OOV_Y/) M^+LY$6NP^8E"<;`GTQ^^V]H1C3LJ3=AI>6OB;4:L#,[`T>Z1&OQV%^-O;Q^' M%[$?WVSC#$2C+Z+K*>H-Q4>V' M$`_@)!B&ONL%'@KL%_KA)-N%N)YPS-`>R>:8[5(#4B"`^H`!*+X4$*)_MH;= M7T_DJJU]X[4Z1VO_:OR?L./^^EC`__?W=_9L_K^_]^*)_S](_@>,$L89!(\9 MYM$U#8;?;@S]"3V3<<[_W__U]G9VWWC_'4X8+P[GDYR4$-N8T% MRZ4#8]6"%*#.?5#6PU=E+#8)[-%>ZQKL,$`R!R,(.93'42 M):DW#C\&8XC^IB\MQWCHE?=6E4ALE.0`_TOK_+3?_=!LMKI=@H:#),C:PG@9 MA M15_2#9*!K5HM&&EMX\U.;7MW9\?[_@L[G'E[%>N,ZN7O[BJSY_SV)!E`:B%8UK0;&%.,L6%VQ' MQ4^M/&^GZN9GR`Q@VU^6$^@##\:#B-^(*MA+#J.>:AT_Z7](UOIB\C^H>S+R M/_OG2?Y_6/WO2:-]:FA_Q0-SGX8++^M2B1=\TN=^G?0/)ZM[//Z7H/_,^7_O MY=X3_3_(^9\G80S1E7;,!).K:`S>$>(P@,=N#!(.,20HVTDTF85@ZWX=39^G M[$AUL_5$^E\Y_5]]0?I_O;.;I?^G_*\/\E&!>2$4_#;\,;1S2IN_K;Y:)D%, M]#9OC?D#^01^@WR>0O29RTE:][:VMFK>:?K?ZZ@NC$OXTJY#GQB7H12_U[+8ZW5`7<`VCJ4)8T&\UC/J#:#H- M!FD?DE'A-U3G.K2-8!I]&0R;2O#;LAQA<,!?_63 M(+V*AE5M@&L5\``C-5>_]=<>Z[AQW#]I]=YW#OOGK>-6H]LBUQA-^[0K/9>] M'?=7Z3OC5$*Z76@(IJ@.Y5>Q[4/;4TS"SNU4DV_270Z=Q%5!'C;)&`\:,N5$ M_S&756;SI@MDMP/(8DWM16,'2$A("@2:70 MUIQ?":S=T&'/NI3.F]Q'B'OVS-*X/#:#D@^\S?X&L9)W,&C);SL4W@K>&"W_ M32'G8;L)B:0;Y[]B@"E9K<+=(\T1H3?D#\);P7A';M)I/)C=5-F3NK?^/3C5 M_HA.!?#<3\7SC\'-CW(Q?@F'Z=7WV_#,412B,8R"^$^WQ8]%3;X/X.YS MM6VV*"5>Z::7;KDL',HW##F[DK,@YAV4@<<2K6L.G`M;?O/ZY9L7KU[NOBG? M?#OY:1Q=^./3;;L4R\`^-7I1&+P$ M^D(;.C6K)5VR$2>9W7I(!V$*C:%'[QV;RC*6NS;EA-?N+>%U'ET7M[1FM[4M ML8,0-F";Z_"^4&EG%;BTU!;SA$TKQ::]%\NA4TOB4B'WVS;XV7:"5K27\-;[ M%KRJQL$4?M2R6Q'EN;1&N?/Y3^/Q9VVD=7YFRTY>'^_=3R'-\U:CU]*#)>WP MF`;H]:\FPJ:E3B:BK"'Z*>=V34R\S5E%6@-)H.7Z^6^8$C0&2Q+R]4;-%$W9 M#!0FN!QP$Y5C<$@@*@[3%J) M7`A1^V#9 MD*2PCJ!5Y4$RHW[('D$8>AAH&JOG/,\L&NE14FXR8Q)T>!4E*4Z%E:6&J]@7 M/D^"\665L8-G6I<"YTK1QA&:#LF0+]2,!\TLB((A3#0W&W\U(7,WIL[B^LKB,/>[K3YLK23GR'B M;A4&X#U[!D%RQ6JH0J?!YQ1+U/#L;(.B,V-LC=?C:NS43S[*58(0+0IQ<&3Y M$Z586*PXFQA$`>7AKLQ(/IR=$UBD/FZ"LGR5#NL;>]ZFM[LC>Y-5J`/&@9(@ MY4_)UJGNR:HU5?=W4UWC6*,?]#7:$/3%QJ/FK`4A#B435.3PNQ7<8^WWIUN[ MU=[_932_#WO_M_MR_W56__]T__=`]_]KE<99&W?3"S\)!W"[/YE/>;(3TG@: MV_D'QLR:XY#)TQAX)_<"43S.O4%415";NHU_C\,+O%U4]XMJ8\CXISI>.=X9 M^XF>1W3=F-=ZS579+<7*)G9J)>IP_;NLM.OLR5*,:N/<^7S0?./J*"-:>BO\ M(+]G(UV-0/7NRZBT%_:[DAL@PQHNBY2/V3@&^+_F+7,O9F"+[G]?[-OYG_=> M[3_%?WG@^U_39ZK@ZM=Q4;QX%Y"/R8@C[R6<#O+>I>$DR'T7^[/$\5*T)YDG ML%9P`V@<'W>:BI-7(=$DP\6:O,\V&,%(NBG`,Q),>3G]C#?J3Y(1_DC^IKP/ M.@?_V6KVE/.!O/+B+4C.,M*<(?0&,NX+ZJXMYRBO&=VCV,]#-0##N@CZT:<@ M1G\$QZ&>9\SCF34"'@^1G:;8S#QV/AI<>`3$P'F&.3V(O"@S7\2%I\Y5>0'8_N5\/*YFCH6L M-]7&>?NG]^QOJ]EJ_]PR7PGUV#,V;/P7!K"<)LQ8951/B""8#)^4%Y%;*28` M#LZ&%)%OV;M[;=DOP_&X#[^S:'G$7B6H'X+W))42?L!3/Q5(R3$1,(6ARI8C M/G.(+JJS:)J$%^,`Y5X<,B@(YC.=A+UPRBHE`7AZ`ASF<;`*Y):SK"YG.6+X MLU5RO;)L;B.=LH1FHC2=V:RR[CVKZAPN_'NME+HBHW65/FHF@PD2C!%/GEW8 MLHURMKZA7-!/%^R-&*I)(;Y%8\8,KP/2%;-Q4CA#7I.0#VT[1N&GP)LGX#9% MWEZWPCYM![AO_#-A<`=L)(,/,7+0&96P]\`K$+';:/",P-9\K(&>00%^,%SY M%$;SA&TW(',/)?G#CD,U956G'C&:N]%;+3("W,%<-<="BVB2#3 M$!^T^=08=E&"IER7TBP*H0O0`O?5\C)C9B/5]O1W2U]"0F4@?:B_ MX(*E#!N_PP!HJ]/WM_6%%IIF_I"2Y.=0ZY?B-KS;HH2.P%G8%HOI6HF=\&`< MR%$DGYU$P_DX6)3KT>6\3&A2?J(FY0$!<5C)*Z+F.$IL(B.X.XZ=1$L+(:6[ MV.MB%C"6+/A^"E*2=E"6YY%-G-(9;]-C4@\[("8BODDX'0:?;6AJ9S,.5/VT M5G*P*O8)]E'V9I;?)T$5[\KIGK>D_O_JX?7_.[N[ M.SL9_?]3_(>'^12H[H5[Y_M6XXPXLNGE:3Z7+_*W6M*TO]#N3`NX/Q6NYK?F M;7M[-?RS5E[(Y\$KBMDO+Y0G`?+7M]\HWZU"#GI'(NI&IC[OE@=G=D>$=]8$ ENS->"(*9RD`?AN.IL>A/7O]/GZ?/T^?I\]5^_C\`W);'`#`"```` ` end |=[ EOF ]=---------------------------------------------------------------=|