From eb692444af43e1963095138c5a0767fcc4fedff2 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Fri, 11 Jul 2025 10:46:25 -0400 Subject: [PATCH] Thumbcache artifact added Added thumbcache artifact parsing --- RecentActivity/build.xml | 3 + .../recentactivity/Bundle.properties-MERGED | 18 +- .../recentactivity/ExtractThumbcache.java | 248 ++++++++++++++++++ .../recentactivity/RAImageIngestModule.java | 2 + .../windows/Bundle.properties-MERGED | 3 + .../thumbcache_viewer_cmd.exe | Bin 0 -> 21504 bytes 6 files changed, 260 insertions(+), 14 deletions(-) create mode 100644 RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractThumbcache.java create mode 100644 RecentActivity/src/org/sleuthkit/autopsy/recentactivity/windows/Bundle.properties-MERGED create mode 100644 thirdparty/thumbcache_parser/thumbcache_viewer_cmd.exe diff --git a/RecentActivity/build.xml b/RecentActivity/build.xml index 4b3d8e33472..11960ef2987 100644 --- a/RecentActivity/build.xml +++ b/RecentActivity/build.xml @@ -25,6 +25,9 @@ + + + diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index bd7737dfb9a..bace77e63b9 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -4,15 +4,10 @@ cannotParseXml=Unable to parse XML file: ChromeCacheExtract_adding_artifacts_msg=Chrome Cache: Adding %d artifacts for analysis. ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis. ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s. -# {0} - module name -# {1} - row number -# {2} - table length -# {3} - cache path ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3} DataSourceUsage_AndroidMedia=Android Media Card DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card DataSourceUsage_FlashDrive=Flash Drive -# {0} - OS name DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0}) DataSourceUsageAnalyzer.displayName=Data Source Usage Analyzer DefaultPriorityDomainCategorizer_searchEngineCategory=Search Engine @@ -26,7 +21,6 @@ ExtractEdge_process_errMsg_spartanFail=Failure processing Microsoft Edge spartan ExtractEdge_process_errMsg_unableFindESEViewer=Unable to find ESEDatabaseViewer ExtractEdge_process_errMsg_webcacheFail=Failure processing Microsoft Edge WebCacheV01.dat file ExtractFavicon_Display_Name=Favicon -# {0} - sub module name ExtractIE_executePasco_errMsg_errorRunningPasco={0}: Error analyzing Internet Explorer web history ExtractOs.androidOs.label=Android ExtractOs.androidVolume.label=OS Drive (Android) @@ -59,7 +53,6 @@ ExtractOs.windowsVolume.label=OS Drive (Windows) ExtractOs.yellowDogLinuxOs.label=Linux (Yellow Dog) ExtractOs.yellowDogLinuxVolume.label=OS Drive (Linux Yellow Dog) ExtractOS_progressMessage=Checking for OS -# {0} - sub module name ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files ExtractPrefetch_module_name=Windows Prefetch Analyzer ExtractRecycleBin_module_name=Recycle Bin Analyzer @@ -75,6 +68,8 @@ ExtractSru_process_errormsg_find_software_hive=Unable to find SOFTWARE HIVE file ExtractSru_process_errormsg_find_srudb_dat=Unable to find srudb.dat file ExtractSru_process_errormsg_write_software_hive=Unable to write SOFTWARE HIVE file ExtractSru_process_errormsg_write_srudb_dat=Unable to write srudb.dat file +ExtractThumbcache_error_finding_program=Could not find thumbcache_viewer_cmd.exe program +ExtractThumbcache_module_name=Thumbcache Analyzer ExtractWebAccountType.role.admin=Administrator role ExtractWebAccountType.role.moderator=Moderator role ExtractWebAccountType.role.user=User role @@ -170,21 +165,15 @@ Firefox.getDlV24.errMsg.errAnalyzeFile={0}: Error while trying to analyze file:{ Firefox.getDlV24.errMsg.errParsingArtifacts={0}: Error parsing {1} Firefox web download artifacts. Progress_Message_Analyze_Registry=Analyzing Registry Files Progress_Message_Analyze_Usage=Data Sources Usage Analysis -# {0} - browserName Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0} -# {0} - browserName Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0} Progress_Message_Chrome_Cache=Chrome Cache -# {0} - browserName Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0} -# {0} - browserName Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0} Progress_Message_Chrome_Extensions=Chrome Extensions {0} Progress_Message_Chrome_Favicons=Chrome Downloads Favicons {0} Progress_Message_Chrome_FormHistory=Chrome Form History -# {0} - browserName Progress_Message_Chrome_History=Chrome History Browser {0} -# {0} - browserName Progress_Message_Chrome_Logins=Chrome Logins Browser {0} Progress_Message_Chrome_Profiles=Chrome Profiles {0} Progress_Message_Edge_Bookmarks=Microsoft Edge Bookmarks @@ -247,7 +236,6 @@ Sam_Security_Answer_3_Attribute_Display_Name=Security Answer 3 Sam_Security_Question_1_Attribute_Display_Name=Security Question 1 Sam_Security_Question_2_Attribute_Display_Name=Security Question 2 Sam_Security_Question_3_Attribute_Display_Name=Security Question 3 -# {0} - file name SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}. SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine Query Analyzer SearchEngineURLQueryAnalyzer.engineName.none=NONE @@ -260,4 +248,6 @@ ExtractWebAccountType.parentModuleName=Recent Activity Shellbag_Artifact_Display_Name=Shell Bags Shellbag_Key_Attribute_Display_Name=Key Shellbag_Last_Write_Attribute_Display_Name=Last Write +Thumbcache_Files_Not_Found=Thumbcache files not found +Thumbcache_process_error_executing_export_thumbcache_program=Error running thumbcache program UsbDeviceIdMapper.parseAndLookup.text=Product: {0} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractThumbcache.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractThumbcache.java new file mode 100644 index 00000000000..24a06075228 --- /dev/null +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractThumbcache.java @@ -0,0 +1,248 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2020-2025 Sleuth Kit Labs. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.recentactivity; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import org.apache.commons.io.FileUtils; +import org.openide.modules.InstalledFileLocator; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.autopsy.coreutils.ExecUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.ingest.ModuleContentEvent; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.DerivedFile; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; + +/** + * Extract the thumbcache files to a temp directory so it can be + * parsed and added as a derived file(s) + */ +final class ExtractThumbcache extends Extract { + + private static final Logger logger = Logger.getLogger(ExtractThumbcache.class.getName()); + + private static final String THUMBCACHE_TOOL_FOLDER = "thumbcache_parser"; //NON-NLS + private static final String THUMBCACHE_TOOL_NAME_WINDOWS = "thumbcache_viewer_cmd.exe"; //NON-NLS + private static final String THUMBCACHE_OUTPUT_FILE_NAME = "Output.txt"; //NON-NLS + private static final String THUMBCACHE_ERROR_FILE_NAME = "Error.txt"; //NON-NLS + + private final IngestJobContext context; + + @Messages({"ExtractThumbcache_module_name=Thumbcache Analyzer"}) + + ExtractThumbcache(IngestJobContext context) { + super(Bundle.ExtractThumbcache_module_name(), context); + this.context = context; + } + + @Messages({ + "Thumbcache_Files_Not_Found=Thumbcache files not found", + "ExtractThumbcache_error_finding_program=Could not find thumbcache_viewer_cmd.exe program", + "Thumbcache_process_error_executing_export_thumbcache_program=Error running thumbcache program" + }) + + @Override + void process(Content dataSource, DataSourceIngestModuleProgress progressBar) { + + if (!PlatformUtil.isWindowsOS()) { + logger.log(Level.WARNING,"Thumbcache only Supported on Windows Plaatform."); //NON-NLS + return; // No need to continue + } + + String modOutPath = Case.getCurrentCase().getModuleDirectory() + File.separator + "thumbcache"; + File dir = new File(modOutPath); + if (dir.exists() == false) { + dir.mkdirs(); + } + + String tempDirPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), "thumbcache", context.getJobId()); //NON-NLS + List thumbcacheFiles = getThumbcacheFiles(dataSource, tempDirPath); + if (thumbcacheFiles == null) { + this.addErrorMessage(Bundle.Thumbcache_Files_Not_Found()); + logger.log(Level.SEVERE, "Error finding thumbcache files"); //NON-NLS + return; //If we cannot find the thumbcache files we cannot proceed + + } + final String thumbcacheDumper = getPathForThumbcacheDumper(); + if (thumbcacheDumper == null) { + this.addErrorMessage(Bundle.ExtractThumbcache_error_finding_program()); + logger.log(Level.SEVERE, "Error finding thumbcache parsing program"); //NON-NLS + return; //If we cannot find the usbParser program we cannot proceed + } + + if (context.dataSourceIngestIsCancelled()) { + return; + } + String thumbcacheFileLocation = null; + for (AbstractFile thumbcacheFile: thumbcacheFiles) { + try { + File thumbcacheFileName = new File(tempDirPath + File.separator + thumbcacheFile.getId() + "_" + thumbcacheFile.getName()); + if (thumbcacheFileName.exists()) { + String modOutFile = modOutPath + File.separator + thumbcacheFile.getId() + "_" +thumbcacheFile.getName(); + thumbcacheFileLocation = tempDirPath + File.separator + thumbcacheFile.getId() + "_" + thumbcacheFile.getName(); + + dir = new File(modOutFile); + if (dir.exists() == false) { + dir.mkdirs(); + } + + extractThumbcacheFiles(thumbcacheDumper, modOutFile, thumbcacheFileLocation); + addThumbcacheDerivedFiles(modOutFile, thumbcacheFile); + } + } catch (IOException ex) { + logger.log(Level.SEVERE, String.format("Error processing thumbcache file %s", thumbcacheFile.getId() + "_" + thumbcacheFile.getName()), ex); //NON-NLS= + } + } + } + + /** + * Extract the thumbcache file to the temp directory + * + * @param dataSource datasource where software hive is + * @param tempDirPath temp directory to write file to + * * + * @return hive file location + */ + List getThumbcacheFiles(Content dataSource, String tempDirPath) { + FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); + + List thumbcacheFiles; + + try { + thumbcacheFiles = fileManager.findFiles(dataSource, "thumbcache_%.db", ""); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING,"Unable to find thumbcache files.", ex); //NON-NLS + return null; // No need to continue + } + + for (AbstractFile thumbcacheFile : thumbcacheFiles) { + String thumbcacheFileName = tempDirPath + File.separator + thumbcacheFile.getId() + "_" + thumbcacheFile.getName(); + + try { + ContentUtils.writeToFile(thumbcacheFile, new File(thumbcacheFileName)); + } catch (IOException ex) { + logger.log(Level.WARNING, String.format("Unable to write %s to temp directory. File name: %s", thumbcacheFile.getName(), thumbcacheFile), ex); //NON-NLS + } + } + + return thumbcacheFiles; + } + + /** + * Run the thumbcache parser cmd program + * + * @param thumbcacheExePath - Eecutable for the thumbcache program + * @param thumbcacheFilePath - output directory for program + * @param tempOutPath - file to parse + * + * @throws FileNotFoundException + * @throws IOException + */ + void extractThumbcacheFiles(String thumbcacheExePath, String thumbcacheFilePath, String thumbcacheFile) throws IOException { + final Path outputFilePath = Paths.get(thumbcacheFilePath, THUMBCACHE_OUTPUT_FILE_NAME); + final Path errFilePath = Paths.get(thumbcacheFilePath, THUMBCACHE_ERROR_FILE_NAME); + + + List commandLine = new ArrayList<>(); + commandLine.add(thumbcacheExePath); + commandLine.add("-O"); //NON-NLS + commandLine.add(thumbcacheFilePath); + commandLine.add(thumbcacheFile); + + + ProcessBuilder processBuilder = new ProcessBuilder(commandLine); + processBuilder.redirectOutput(outputFilePath.toFile()); + processBuilder.redirectError(errFilePath.toFile()); + + ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true)); + } + + private String getPathForThumbcacheDumper() { + Path path = Paths.get(THUMBCACHE_TOOL_FOLDER, THUMBCACHE_TOOL_NAME_WINDOWS); + File thumbcacheToolFile = InstalledFileLocator.getDefault().locate(path.toString(), + ExtractThumbcache.class.getPackage().getName(), false); + if (thumbcacheToolFile != null) { + return thumbcacheToolFile.getAbsolutePath(); + } + + return null; + } + + private void addThumbcacheDerivedFiles(String outputFolder, AbstractFile thumbcacheFile) { + Path outputFolderPath = Paths.get(outputFolder); + List files = (List) FileUtils.listFiles(outputFolderPath.toFile(), null, true); + for (File file : files) { + if (context.dataSourceIngestIsCancelled()) { + return; + } + + Path candidate = file.toPath(); + + if (candidate.getFileName().toString().equals("Error.txt") || candidate.getFileName().toString().equals("Output.txt")) { + continue; + } + try { + final Path caseDirectory = Paths.get(Case.getCurrentCaseThrows().getCaseDirectory()); + final BasicFileAttributes attrs = Files.readAttributes(candidate, BasicFileAttributes.class); + final Path localCasePath = caseDirectory.relativize(candidate); + + final DerivedFile tcacheFile = Case.getCurrentCaseThrows().getSleuthkitCase() + .addDerivedFile(candidate.getFileName().toString(), + localCasePath.toString(), attrs.size(), 0L, + attrs.creationTime().to(TimeUnit.SECONDS), + attrs.lastAccessTime().to(TimeUnit.SECONDS), + attrs.lastModifiedTime().to(TimeUnit.SECONDS), + attrs.isRegularFile(), thumbcacheFile, "", + "", "", "", TskData.EncodingType.NONE); + + context.addFilesToJob(Arrays.asList(tcacheFile)); + IngestServices.getInstance().fireModuleContentEvent(new ModuleContentEvent(tcacheFile)); + } catch (IOException ex) { + logger.log(Level.WARNING, "I/O error encountered during thumbcache processing.", ex); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to add thumbcache as derived files.", ex); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "No open case!", ex); + + } + } + } +} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java index dd528087ddd..94182c498a6 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java @@ -81,6 +81,7 @@ public void startUp(IngestJobContext context) throws IngestModuleException { Extract webAccountType = new ExtractWebAccountType(context); Extract messageDomainType = new DomainCategoryRunner(context); Extract jumpList = new ExtractJumpLists(context); + Extract thumbcache = new ExtractThumbcache(context); extractors.add(recycleBin); extractors.add(jumpList); @@ -98,6 +99,7 @@ public void startUp(IngestJobContext context) throws IngestModuleException { extractors.add(zoneInfo); // this needs to run after the web browser modules extractors.add(sru); extractors.add(prefetch); + extractors.add(thumbcache); extractors.add(messageDomainType); browserExtractors.add(chrome); diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/windows/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/windows/Bundle.properties-MERGED new file mode 100644 index 00000000000..c4014329aa4 --- /dev/null +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/windows/Bundle.properties-MERGED @@ -0,0 +1,3 @@ +ExtractThumbcache_module_name=Thumbcache Analyzer +Thumbcache_Files_Not_Found=Thumbcache files not found +Thumbcache_process_error_executing_export_thumbcache_program=Error running thumbcache program diff --git a/thirdparty/thumbcache_parser/thumbcache_viewer_cmd.exe b/thirdparty/thumbcache_parser/thumbcache_viewer_cmd.exe new file mode 100644 index 0000000000000000000000000000000000000000..8b083ca8e88c3aa3093ab57bee2ef804937bcd76 GIT binary patch literal 21504 zcmeHv4SZ9_mG9V=u`F!lG(V@!o4%Z!cUO^=)BAD&I1dEUZj z=CP-;UtKhsR{!dvX0baY^alf5g5Ex%!|V44Bw@2p2!{Paw_m8ar%C7wbo$D2b2Yj+ z>GV4uI=*5>-eZY$k2CUE4eIwehaOwW-(P;LiNE)8xOeCMkJWPg8y*|r@UO-3cJX&( zcZW!{uIAiR%UFF{CcAh;-ugt_d6u3wH!Yj757`4>I1JR~;~WmHaQER(8u5^uH_ z6)5e=J_h}{cX33*r4Ia^d=Zc}uVc(kg?`44fZjFFPR2TE68@d%@#d@M#*GeXvv(?! zd;=1iYi>tIe36`2jUzC&sVvy(mAs4{z6I@Wq#~qJq?E^vmNEs6?IxqyghaB`A*DQS z#>UEmpq^q|BM=FSl23& z-sOS|Za0fAEkqs}SM{*y)JTmk$HN<2n@-f|$RL>flW2`j9hp*XT{rSq1c;uc%FT7r z9}J`6?p>ADYu2oha^yJxZ=0+mAWET%=)4}*!y2080u@H+VI$4DoW?luB++f(ZIufy zDjc*M$2M*_mQp{GhWe>>(TkA_Q@bu+#6mp4Ho2K;e0Wedfm%~^V@^w}X-u4x&X{Lp zk8TM}wBdM-u8<*{PuZ=M-ICUY9;IlUa4crb^g8d*I5Ma!!#eVG4e!(GQJ4_W7_C)D z#tS1;Y1*MCP>tSp4$O%q%iT`!!> z4d2i4*K+)MGpLG(d0+XA2QmtfmW%A+BA1}5h#Z@v*vEB5zX?brI=vzKD_z6tsD&%G zsFoB8QA~2HxRWu~lhN9fA)>GoV_Bgxk6cg$`J(3{C*|QbU7_uqcJQCzvRX&#b-IS0 z9GF!zY>w=%-Za+OG`vG+Msg#SAk{$nHr9pKjk{}f?RalO>XZw}?048bUN%|siZ-^Ogr>>4!S!% zGWJqr%C_w@+VAgWWta@zW9OIhAtGZR*rp!rh&Df~TKN(A9m!!0jU)RM8zF`p6^6lQ z>=sN!PFn0nykOKhcxlsOWSjFyo|SdBDeV_4WLchk`%khg=2e500y2?F?R;qOa80r< z7h8S3f z*7>}ef$A8wYIs$J>I7B)h*^tFWuiJx$QOBa4yqTa`g2~*Lbb3C)z^4cjcN^5X~rW{ z*{IqH`3SFSQ0=GI1H3vH)xA`Glvi_5Jwnw8ujXop!YGJu5vZSo7Lsh*p~H}JTEca_ zDJkI!UQJ3^!K+CL`TCzqN;sdll7xB0rG{5YLiH+C|A=uSQzRjm;Uce+gj|N7^D0Tm zWq6HOHQJ#u6cjn0MQNJPzvT!?KEJ`MNj|^Ct4Th0@~R5vKr<1OXd1FOlQA(eH3wy4 zDb_~pWpRFDp2(Csyn-_mtBD&y7N7N)1*-~ZG{-SmTsq2}>}`>$43tUs*bUePQRZYb z#WsMF#s*(ug~f*TrZErvm#z^$qb=c@@D_XWuoZ9ez-Gx!MR&kyp-aq17VdKbwDp1mUMRcjTW&Db!>(pfOSnA-Ao*Pf;h@;6?gO2$hbPD5f8`9D#cm8VS1fb z8hO-_L0utiL%Z)~&q)4bSdDDBSyv(#w&=;AY+W zp1DxE0?rv3w3VmxJ()y~*6YZ1MlVG!-y+SATrQIGBbOIQ+Q{XFlI9r!{Ueu!aON{P zyafL$qHp9f{A)We+@c-)1$2#$+QFaj!UFB!DHOn$2Sl_(zlCxjqjq#RNE|(~8b0K! z)D^2wJE}{DEc100qCm^BI8$YCnL>XWcl9F_^#rXy))cup&1fA} zvw0ORm{7>*N_5Pe}{BA4^SSx5Ilkk}otT9gk_`Bqpa%4Cc& zn7VNEIh3T#qbKodlqFpu7EK!!+(?24eAG2~#B*p(tR5^^^p?3i8(Z}zS>UBjF51fNb<6)(~sR4+fL}4Wzul3p? zn})GnHh$_Jv@1@5SZ)!YBW})$6Z{2XOW;v>r~^S&z=-1TKO*UL#L+~>zsU07n0BxZ z)WjSQ@5#^XXAybI^|}%SygKwKsTN6ga8W9sZK-ZjWIn6Nd=|2l;*icq@gVGq%Zz~L z16e!t37kg7-NX(*h(fZ--)1vxm-%6Rk98K2tyha|om7Jk9a3=_P=w$JA()2zX(s$> zc==U?a0_=10$LUQf!MU}tlA-2oz&gXI34;i7tz(~Xj9JIAB2~-h|P*3cBbkhL(=J? z5G$OovaRG#UO-nmL1#ysR0zBEu>z7QJ6f&6(E3vg*C3uFaKQwb0peK9ah(4=Foa+=&VM1s&LjTw zz<(ZWD6dgeHIQ*k_af!zhclzqMX`)%b-qU|0RsBfUl&Orq(YBaqaYQfAYFe#Z;Dn6 z(bYws2C*$M@Otck#4aCm4n~hw>pY^wn-QoF>P8?rUXLg*`jCMdg+`>tuj3u({qDxZ0BZIofQTK>@Xuvc4WH8%YksI z>R=i%iZXpTEB4$NKPzA^#YXWkr~SeQ_?!e1m69$mq{0f!8(EQq`#U--jI7PUd5Dgl zr4yrCh*YmDO)he7?JW(kh=ilS;Z4AI0iv{(E-CVQ(t6lk$H^z+Fva}=_ewVd38ZR3 zs*q5K2007fn1)7poqTeA^dSU9)$ll=kss%Zx?x@O5KPz5NbCt{EY zJT~%~Jjn}Ii=wyD7(MBtUg=~G_N2GSZ}g~JBgfNw)F{Tvm5#>9G2AfQm~zyNK7<_1uy^EBMuDDK91Kp6!}&x4pCW`ML)LoL=u&+-OQXaaZpbak+KaU42`yXnYyNo+09o0aL=!)Y``>x8(n=`KMa zDm;3rCoDJD_7O$fo&>!Vg}3SK3CTxruL2Cx2?;+Whh3!fRySx+Pu50*)$V=(r?}<-=B^gT`9#OfX z;+IV#P~6~@e}|#iNGicZ=fi;LnvSGtXoLc2;_xaZ{!ej2v10MOQkc(~$93qtsK+uz zbu-_U+jIg3n~uivlksa44`JOD!g7s}UoCl0mchnF8i3-m*aoms)S&c{&!CHVlJBb6 zaB%?uH`o|^8-`fqiMm|LNLB}bq7N`zXOs7m^6CH5^_#SvD-_4#v`Yh52 z(qg0mq=%6nMPgZLS?O7L7-d-*D*npK%FN1|qZDUvx8>3Y*l_y49-I75Ivhl~$;~DX zsqkU8vjZm#m97-cvSkx02dO9)Q6^AUNG%29ZgQ0Q2TM>J8hcRPqn>yMHxwI=_2h|D z%^m;)J(jjp*D ztG^OOaI7+o#nv3Kqd2mY0K2U86Poa(_UXF9U6<#kF(%!LcRFA{iHr_H?s;2JnO>>f zJ%wUyUSvR;xAi%yPoS=S?4{^SJ!uDteYxOqH-ZsaF4%|nh7AXIKnn+VB9jaD0us&b zMR`~)7wq9Rk=G9FrV2aQkJ?0QI%5-0LzfXp{w6m#$PvR9>OTmi4HH4$l7|*KoJ|Am zB(Q1h*u=wVnS27D6r(bJNU-Z7eX&32_F}kPJLLsd%5O7MZqZZjC`I12r(ivQ<<|4p zZ6*AbspGH30)H(n!fXGRGCg=jYuUjX{#G5V!+WGweXyD8L+7u^@`=p7;4KaR9iYa8 zHK?7)v?9+|v9S}GcHSEQeZ%n3IdnOo0&IBbEfnPfw;P>y1JIK;@kvOup$DSZ04NH} zNegu-MjV8|!fN2$PB^~y1>1$=LfVs<-1edohVA_I9DVO`Xn?+`M- zJYOnsETY#uM=`y!9L)XIv~cFcn;BS^V;f&qG4W3Z8j%Kveox7ZPomV5FBY!DH1(!4 zSTW6P)bbSI@|g|8wHN4vPL3SL{2l>pLu?CH{eBSK5b<4!qswQ2CtJuc&>R^je?fyO zvV+{jTB!8>bGMGYe{MtFMu1yJC*XQspsY1IWjh|yMzgSF)8*5V6Dr#W!QZ#v$FVLE z7G<+y$%}iz>8TlyqVhHB_*zS&b%s%w+cRE@udv^lUO!69tE;lE5MRWiFK;}SLLoV2 zb@q&t+NYQ2H??S=eqfI2_12LZb>Yx^TW^g%m=mqbv0V;kN7t(}ero$;P;+-=ti*OE zIKlUs8RSlJdOaVAR!o?<7>lU$0Mx}ik&9}$SpEweL+zC5AE5n9od>CD*f#;qQVEd& zW_bx+XpF0%qIEE(r3AxQBx>>S7ByOIQMc};iG}Zkj)_!bL%Si1MR_-Q<8Vz4Dg{R~KJ<6s_a)CMiw(>V*0iK=kYTq^0UNT*>y&ukc z;T}+q%^mvJa8~sAOXu_EUK+NL0Pmj}w$K3gwSVfBWAftt=q4}zCeIJ?{4meI!}C#| zKgaVio=+kF`1yZV1AAK-`v%exq;aIvNWVe4f~0O`>?Wk!k(@{^NPdI}>yhe^%8+hB zy0U?>^GK(VMv=aT^eEB}q(QXPQ@EZn0jUhB9H|Cr9@2eC64Gv@$B_;reIMy0($A1C zA*n!T9#RpK4yg>ujdU+kFVYq8^$yY*q#q!?fbppwnaem`r(KzL>t-?Bx4FaHA^L=tZr?UvP^ey0BjDH0ecn!=&=vIggs{I8 zUdVL*|;1z9ZqD`V9JgzbT_u+8h|l8kE7HKz5T)QDj^xNy!97W*9JOH^e7aF^^CgI&RBe z>GS)@5YQr!iNt4-8aq-Ng^mv~o0LAE&R(^}9|-zpn1HZN^!bG?%1C4%2@|DJ{MoPz z`eT7!qy-5*r}DPWf4@Jl&HrJ%7!+0v8$DQ zL(qa~C4~F$PA8ccOVJ%)e^Vkh0GJ{>tqd+yDoj}qGw|YeKLa*%yqR=o*dy^V!!4)OD^kkV zGD8y2pr7u8ZGt~`vI3`_wg{(P>Xv$aPHqy4jbLs$$i7TL*c|BG?p%Sb%kS-j!(AM* zZw?B}oXL$M0chs_I8pnMbs&^b@E4*jr#Gw+o5sHGYPe31S#3Va0FP{WFi1?dDFelG zM8#Ndn*%}Ykq(2<;p^=U^?N(8dph(`Nf4>ixe77+0Jn6i#aWE=o&+mCsj2(Z*i;p< zl1()TzJ+UAz#pg~-VAOR>bxOwCVI8#>$pD@?n^@Jlzsdf_^RaAEi_3%FrMtOiUMS% zwhWSB#^Yb}o=yPUk?-<2#uk zdJ$Io5c75gaI&Qln|LqmWH>SM%?~_#{o7M|DXi5ZXx{#jxI&EG*@NwvLfU|`tI}Cz z23N3WzQ2aB{t#MBAPq1Hxu1mq3*cP^ z=wepJ%z%*YQ|1~$0r7I}SRQX@=k;4~rX?qEZI_fa#`zw=+MwYQ`>k^7t`bNL^8i{n z9n3uU4mgZARxd3#$D{VRea_PQk?m)Cg5y?H#x}D)`lDn#W%xluKik5~!=AM(b2Ybc ze#-#$$KhpYUBx=NJ|*b03(3cV&}ax+4x&bwiiQ>KGKw*1si!8=1(sv{|KjW5q_&Yq z;#}cxsN;?s{gdACBbI5*={`z)Lw*|GnnIncivQX1u1hp!D9}$1YwzxZ%>_Fg*C9nAk%nsVbiZ1w-|1C z66Cgl4DXAvX%BVAH`HZ3Noo}WuY_Kc-HQ-u8^l_J*(+0C9n z+Ka#SPduHiD7hJ%N`bu%m-7{A5bVBS8w_@D4kI+BIml>4!m_1=(F8w>_8>S3x^D>sIzBcCAtt^==T)xCj`uMStJ;AwSjS_Pw`-0$dE;!?%KzmnbH)#G4IQ{MD z-ADgo2xI@K)Vq{g54iJpprO4#ZW!kkn(f~H{&u=G!mU2W4odBvzRlq+TYSNGF%Y;P zu>^AwpAcB`x!oI_7zZpo&pF!prEbk?HpQ^(H@6vN&~$aUl3thpV!~vt444L8CX|3R!DPq$Ne0@ z*x#n`(#$7+RVTj4q(I{w0(=kkZQ35fZEG{GAC`w`Nm7X|A^V1@T+dT}4Eu9vfW9|> zcm9ht*v9xn`x@<=A3y)KG=QH7;YnkK$dPtZjh+OYYy#IzI|)Wl0*+r5F^bjJ;|S4` z&ak8LW@h(>LcYGuz1xL>zFvRGQ5+8Xt3o)w^m#)|`?@=Vfl#1JS_=PO4i5pt`ZSIPpowQCx{{)kfUd8q2K3^e=jmpn>4>V26lJ zzk@z7If@6YiXW5K)nVOi+Fa>1_zYH`X)~$iYHs3u+Lv8lf@wIH zb-KUPv)r@1re@XJmFw5X3m@12bNZlv6YM!GZjC-qAJjjp|8M%|^~(%qgWXVL*kkxh z!^?)(4ZkqFZ%8-JGnN=l#@)sd8d-EU6vN_vQWm#oeZ`oqmY8kTZvmCTMX?e+V+VV@wJC?NaqVl`StIAiE zuP@(HzO{U)d|&y&^6!_Am%mZ|cKPqiSw&7oVTGkQ?*R4-l$EDHdQ`W`B#;HQ~BM>la)WMJYRW3)q<*$DofRh zDzPe9^}VWRs$Qvjt?K7hZ&&@k>bQ+fLcuwEfn0*`~JNY`@)ZvDes}>|OQ&`{(Unv46|{UHkL)SL{Er|H^*J ze#NeKt(V2FH5GR>zQIzvH0eq~kTmFB}&fe{|$HZ*omF;Cj%t$MuNoo33xUo^l;^9dn&@ zopJrz^{$Hv_}vcXE<^tb{UW_y@6xZ;Z`AkdKcoMG{_Fa`*T0~DRsWWLLjSIw8FCDT zhC2)FHi1{0Q=6`3-vsf&m z<@1&|EiucK#Zmsr^5*hj`PVV4zbaQ(+)+_qac@PqB2uxp;%LQ*ihr(nr(&V?c59t= zt##CT(wbIzL*=r{)s@Yafy(WbyDIlo9<2Q9%7sQg_LsJA+P-DOpKI7R*f-gIcE9~0`;h(1_9yI5+F!CCx4&WkIp$`b;}*vv zhs&|TvBuHr=yz;$JmEOx_>SZIj^`ccF*6g6i;hc4^`471&{NnR)AjlKh5E&Mzh2Vs&`0#U^Yh^G#JIm+3x}+MH`% zXm*$z%&q1z^KtX5=HHqtEOyH-%OT5mEzev2!Sbr*rq}s xy36TKBOkJ;40(opgJ94ZQ1S5Cf&JKg&YWY(v*<1L7SAmAFTnoz`M*E|{|ioq;amU! literal 0 HcmV?d00001