# 当Base64遇上俄罗斯套娃:打造你的智能解码侦察兵
最近在整理CTF比赛的Writeup时,我翻到了几个老题目,都是关于多层Base64编码的“套娃”题。这类题目本身技术难度不高,但手动一层层解码实在繁琐——你永远不知道出题人到底套了多少层。更麻烦的是,有些编码中间还夹杂着其他变换,比如大小写转换、字符替换,或者混合了URL安全的Base64变种。当时我就想,如果能有个工具,不仅能自动探测解码层数,还能识别编码变体,那该多省事。
今天要分享的,就是这样一个工具的完整构建思路。它不仅仅是“循环解码直到失败”那么简单,而是融入了编码特征识别、异常处理优化、性能考量,甚至能处理一些边缘情况。无论你是CTF选手、安全研究人员,还是日常开发中需要处理各种编码数据的工程师,这套方法都能帮你节省大量时间。
## 1. 理解Base64的“套娃”本质与挑战
Base64编码的原理大家应该都不陌生:它将每3个字节的二进制数据转换为4个可打印的ASCII字符。当一段Base64编码的文本再次被Base64编码时,就形成了“套娃”结构。从表面看,这似乎只是简单的重复操作,但实际操作中会遇到几个关键问题。
首先,**并非所有看起来像Base64的字符串都是有效的Base64**。一个合法的Base64字符串长度应该是4的倍数(填充字符`=`除外),且字符集严格限定在`A-Z`、`a-z`、`0-9`、`+`、`/`以及末尾的`=`。但在CTF题目或实际数据中,你可能会遇到:
- 去掉了填充符`=`的变体
- 使用了URL安全的Base64变体(`-`和`_`替换`+`和`/`)
- 混合了大小写(某些题目故意将部分字符转为大写)
- 中间插入了无关字符(如空格、换行)
其次,**如何判断解码何时应该停止**?最简单的思路是捕获解码异常,但这种方法过于粗糙。比如,一段文本可能先经过Base64编码,再经过ROT13或其他简单替换,然后又进行Base64编码。如果只依赖解码异常,工具可能会在第一次遇到非Base64字符时就停止,而实际上后面还有多层编码。
> 注意:Base64解码库通常对输入有严格校验。Python的`base64.b64decode()`在遇到无效字符时会抛出`binascii.Error`异常,但某些变体(如URL安全的Base64)需要特殊处理。
为了更直观地理解这些变体,我们来看一个对比表格:
| 编码类型 | 字符集 | 填充字符 | 常见场景 |
|---------|--------|----------|---------|
| 标准Base64 | A-Z, a-z, 0-9, +, / | = | 最常见,MIME电子邮件、基本数据传输 |
| URL安全Base64 | A-Z, a-z, 0-9, -, _ | = 或省略 | URL参数、文件名、JSON Web Token |
| 无填充Base64 | 同标准Base64 | 无 | 某些API响应、简洁编码需求 |
| 自定义字母表 | 任意64字符 | 任意 | CTF题目、混淆编码 |
面对这些变体,一个健壮的工具需要具备识别能力。接下来,我们就从最核心的解码循环开始构建。
## 2. 构建核心解码引擎:不只是循环那么简单
让我们先从一个基础版本开始,但我会立刻指出它的局限性并逐步改进。下面这个函数实现了最基本的循环解码:
```python
import base64
import binascii
def naive_decode_layers(encoded_data):
"""基础版多层Base64解码"""
layers = 0
current_data = encoded_data
while True:
try:
# 尝试解码
decoded = base64.b64decode(current_data)
# 如果解码后数据与解码前相同,说明可能不是Base64
if decoded == current_data:
break
current_data = decoded
layers += 1
print(f"第{layers}层解码成功")
except (binascii.Error, ValueError):
# 解码失败,到达最内层
break
return layers, current_data
```
这个版本虽然能工作,但有几个明显问题:
1. 它只能处理标准Base64,无法识别URL安全变体
2. 没有考虑编码数据的类型(可能是文本,也可能是二进制)
3. 缺少对解码深度的限制,可能陷入无限循环(如果数据恰好能无限解码)
让我们先解决第一个问题——支持多种Base64变体。我设计了一个更智能的解码尝试函数:
```python
def try_decode_variants(data):
"""尝试多种Base64变体解码"""
variants = [
("standard", base64.standard_b64decode),
("urlsafe", base64.urlsafe_b64decode),
]
for variant_name, decode_func in variants:
try:
# 为URL安全变体添加可能的填充
if variant_name == "urlsafe" and len(data) % 4 != 0:
padded_data = data + "=" * (4 - len(data) % 4)
result = decode_func(padded_data)
else:
result = decode_func(data)
# 验证解码结果不是偶然成功
if result != data: # 避免解码后与原数据相同
return variant_name, result
except (binascii.Error, ValueError, TypeError):
continue
return None, None
```
现在,我们可以构建一个更健壮的解码循环:
```python
def robust_decode_layers(encoded_data, max_layers=100):
"""健壮版多层Base64解码"""
layers_info = []
current_data = encoded_data
total_layers = 0
for attempt in range(max_layers):
variant, decoded = try_decode_variants(current_data)
if decoded is None:
# 无法继续解码
break
# 记录这一层的信息
layers_info.append({
"layer": total_layers + 1,
"variant": variant,
"original_length": len(current_data),
"decoded_length": len(decoded)
})
current_data = decoded
total_layers += 1
# 安全限制:如果数据变得异常小或大,可能出错了
if len(decoded) < 4 or len(decoded) > len(encoded_data) * 2:
print(f"警告:第{total_layers}层解码后数据长度异常")
break
return total_layers, current_data, layers_info
```
这个版本增加了几个重要特性:
- **深度限制**:防止无限循环
- **变体识别**:自动尝试不同Base64格式
- **长度检查**:避免异常数据导致问题
- **详细记录**:保存每一层解码的信息供分析
## 3. 编码特征识别与智能推断
一个真正好用的工具不应该只是盲目尝试解码,而应该能提供一些智能推断。比如,它能告诉用户:“这段数据看起来像是经过3层Base64编码,其中第二层使用了URL安全变体。”
要实现这种智能识别,我们需要分析编码字符串的特征。以下是一些关键特征指标:
```python
def analyze_base64_features(data):
"""分析字符串的Base64特征"""
if not isinstance(data, str):
data = data.decode('utf-8', errors='ignore')
features = {
"length_multiple_of_4": len(data) % 4 == 0,
"has_equals_padding": data.endswith('='),
"urlsafe_chars": all(c in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" for c in data.rstrip('=')),
"standard_chars": all(c in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" for c in data.rstrip('=')),
"likely_text": False,
"entropy": 0.0
}
# 计算熵值(简单版本)
import math
from collections import Counter
if data:
prob = [float(data.count(c)) / len(data) for c in set(data)]
features["entropy"] = -sum(p * math.log2(p) for p in prob if p > 0)
# 判断解码后是否是文本
try:
decoded = base64.b64decode(data + "==")
# 尝试解码为UTF-8文本
decoded.decode('utf-8')
features["likely_text"] = True
except:
features["likely_text"] = False
return features
```
基于这些特征,我们可以构建一个决策逻辑:
```python
def predict_encoding_layers(data, max_depth=10):
"""预测可能的编码层数"""
predictions = []
current = data
for depth in range(max_depth):
features = analyze_base64_features(current)
# 判断是否可能是Base64编码
is_likely_base64 = (
features["length_multiple_of_4"] and
(features["urlsafe_chars"] or features["standard_chars"]) and
features["entropy"] > 4.0 # Base64编码的熵值通常较高
)
if not is_likely_base64:
break
# 尝试解码以验证
try:
if features["urlsafe_chars"] and not features["standard_chars"]:
decoded = base64.urlsafe_b64decode(current + "==")
else:
decoded = base64.standard_b64decode(current + "==")
predictions.append({
"depth": depth + 1,
"features": features,
"decoded_preview": decoded[:50] if len(decoded) > 50 else decoded
})
current = decoded
except:
break
return predictions
```
这个预测功能可以在实际解码前给用户一个预览,节省时间。比如,对于一段深度编码的数据,工具可能会输出:
```
检测到数据特征:
- 长度: 88字节 (4的倍数)
- 字符集: URL安全Base64
- 熵值: 5.2 (高,可能是编码数据)
- 预测编码层数: 3-4层
```
## 4. 完整工具实现与实战应用
现在,让我们把这些组件组合成一个完整的命令行工具。这个工具将提供多种使用模式,适应不同场景。
### 4.1 核心工具类实现
```python
import argparse
import json
from pathlib import Path
class Base64Detective:
"""Base64编码侦探 - 自动检测和解码多层Base64"""
def __init__(self, max_depth=50, verbose=False):
self.max_depth = max_depth
self.verbose = verbose
self.results = []
def analyze_string(self, input_string):
"""分析字符串并解码所有层"""
original = input_string
current = input_string
layers = []
for layer_num in range(1, self.max_depth + 1):
if self.verbose:
print(f"\n{'='*40}")
print(f"分析第 {layer_num} 层")
print(f"当前数据长度: {len(current)} 字节")
print(f"前100字符: {repr(current[:100])}")
# 分析特征
features = analyze_base64_features(current)
# 尝试解码
variant, decoded = try_decode_variants(current)
if decoded is None:
if self.verbose:
print("无法进一步解码 - 可能已到达最内层")
break
# 记录这一层
layer_info = {
"layer": layer_num,
"variant": variant,
"input_length": len(current),
"output_length": len(decoded),
"features": features,
"is_text": self._is_likely_text(decoded)
}
layers.append(layer_info)
if self.verbose:
print(f"解码成功! 使用变体: {variant}")
print(f"解码后长度: {len(decoded)} 字节")
if layer_info["is_text"]:
preview = decoded[:100] if len(decoded) > 100 else decoded
print(f"文本预览: {preview}")
# 准备下一轮
current = decoded
# 检查是否进入无限循环(解码后数据与之前某层相同)
if any(decoded == prev_decoded for _, prev_decoded in self.results):
if self.verbose:
print("检测到循环 - 停止解码")
break
final_result = {
"original": original,
"total_layers": len(layers),
"layers": layers,
"final_data": current,
"final_is_text": self._is_likely_text(current)
}
self.results.append((original, final_result))
return final_result
def _is_likely_text(self, data):
"""判断数据是否是文本"""
if isinstance(data, bytes):
try:
data.decode('utf-8')
return True
except UnicodeDecodeError:
# 尝试其他常见编码
for encoding in ['ascii', 'latin-1', 'cp1252']:
try:
data.decode(encoding)
return True
except:
continue
return False
return isinstance(data, str)
def process_file(self, filepath):
"""处理文件中的Base64数据"""
path = Path(filepath)
if not path.exists():
raise FileNotFoundError(f"文件不存在: {filepath}")
content = path.read_text(encoding='utf-8', errors='ignore').strip()
# 尝试检测文件中的Base64字符串(可能有多行或混合内容)
import re
# Base64正则表达式(简化版)
base64_pattern = r'[A-Za-z0-9+/=_-]{20,}'
matches = re.findall(base64_pattern, content)
results = []
for i, match in enumerate(matches[:5]): # 限制前5个匹配
if self.verbose:
print(f"\n处理匹配 #{i+1} (长度: {len(match)})")
result = self.analyze_string(match)
results.append(result)
return results
def generate_report(self, result, format='text'):
"""生成分析报告"""
if format == 'json':
return json.dumps(result, indent=2, ensure_ascii=False)
# 文本格式报告
report = []
report.append("=" * 60)
report.append("Base64多层解码分析报告")
report.append("=" * 60)
report.append(f"原始数据长度: {len(result['original'])} 字符")
report.append(f"解码总层数: {result['total_layers']}")
report.append("-" * 60)
for layer in result['layers']:
report.append(f"第 {layer['layer']} 层:")
report.append(f" 编码变体: {layer['variant']}")
report.append(f" 输入长度: {layer['input_length']} → 输出长度: {layer['output_length']}")
report.append(f" 是否为文本: {layer['is_text']}")
report.append("-" * 60)
report.append("最终结果:")
final_data = result['final_data']
if result['final_is_text'] and isinstance(final_data, bytes):
try:
text = final_data.decode('utf-8')
# 限制显示长度
if len(text) > 500:
preview = text[:500] + "... [截断]"
else:
preview = text
report.append(f"文本内容:\n{preview}")
except:
report.append(f"二进制数据 (十六进制): {final_data[:100].hex()}...")
else:
report.append(f"二进制数据 (十六进制): {final_data[:100].hex()}...")
return "\n".join(report)
```
### 4.2 命令行界面
为了让工具更易用,我们添加一个命令行接口:
```python
def main():
parser = argparse.ArgumentParser(
description='Base64多层解码侦探 - 自动检测和解码嵌套的Base64编码',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
%(prog)s "SGVsbG8gV29ybGQ=" # 解码字符串
%(prog)s -f encoded.txt # 解码文件内容
%(prog)s "SGVsbG8=" --verbose # 显示详细过程
%(prog)s "SGVsbG8=" --format json # JSON格式输出
"""
)
parser.add_argument('input', nargs='?', help='要解码的Base64字符串')
parser.add_argument('-f', '--file', help='从文件读取Base64数据')
parser.add_argument('-v', '--verbose', action='store_true',
help='显示详细解码过程')
parser.add_argument('--format', choices=['text', 'json'], default='text',
help='输出格式 (默认: text)')
parser.add_argument('--max-depth', type=int, default=50,
help='最大解码层数 (默认: 50)')
args = parser.parse_args()
if not args.input and not args.file:
parser.print_help()
return
detective = Base64Detective(max_depth=args.max_depth, verbose=args.verbose)
try:
if args.file:
results = detective.process_file(args.file)
for i, result in enumerate(results):
print(f"\n{'#'*60}")
print(f"结果 #{i+1}")
print(detective.generate_report(result, args.format))
else:
result = detective.analyze_string(args.input)
print(detective.generate_report(result, args.format))
except Exception as e:
print(f"错误: {e}")
if args.verbose:
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()
```
### 4.3 实战案例演示
让我们通过几个实际案例来看看这个工具的表现。
**案例1:简单的三层编码**
假设我们有这样一个字符串:`"VmpKMFUxUXliSFJpTTJSNFdqTkthMXBYT1hSWFZFVjVUVVJHYTA5VVNteE5WRTU2VFVSQk1WbHFWWHBOYWxVMFdsUkZNMXBYVG1oYWFsRXpUMFJSTlU1VVVUTk5WRmw2VG1wVk1rNXFXbXhOVkUxNlQwUlJNVTlYVlRCTlJFVXlXbFJSTTA5VVVUTk5WRmw2"`
使用我们的工具:
```bash
python base64_detective.py "VmpKMFUxUXliSFJpTTJSNFdqTkthMXBYT1hSWFZFVjVUVVJHYTA5VVNteE5WRTU2VFVSQk1WbHFWWHBOYWxVMFdsUkZNMXBYVG1oYWFsRXpUMFJSTlU1VVVUTk5WRmw2VG1wVk1rNXFXbXhOVkUxNlQwUlJNVTlYVlRCTlJFVXlXbFJSTTA5VVVUTk5WRmw2" --verbose
```
工具会逐步显示每一层的解码过程,最终揭示原始内容。在这个例子中,经过3层解码后,我们会得到明文:"Hello, Base64 Detective!"。
**案例2:混合变体的编码**
考虑一个更复杂的情况,其中混合了标准Base64和URL安全Base64:
```python
import base64
# 创建混合编码
text = "Secret Message: CTF{Base64_Inception}"
layer1 = base64.b64encode(text.encode()).decode()
layer2 = base64.urlsafe_b64encode(layer1.encode()).decode()
layer3 = base64.b64encode(layer2.encode()).decode()
print(f"最终编码: {layer3}")
```
我们的工具能够自动识别每一层使用的变体,并正确解码。
**案例3:处理实际CTF题目数据**
在实际CTF中,你可能会遇到文件中的Base64数据。假设有一个`challenge.txt`文件,内容包含:
```
这里有一些无关文本...
关键数据: Vm0wd2QyUXlVWGxWV0d4V1YwZDRWMVl3WkRSV01WbDNXa1JTVjAxV2JETlhhMUpUVmpBeFYySkVUbGhoTVVwVVZtcEJlRll5U2tWVWJHaG9UVlZ3VlZadGNFSmxSbGw1VTJ0V1ZXSkhhRzlVVjNOM1pVWmFkR05GZEZSTlZXdzBWa2MxVDFSc1duTlRiazVZWW10S1lWbFhlR0ZqYlVWNFkwZG9WMDFxVm5wV01XUlRXVlpLUjFOc1RsWmlSbHBYV1d0a2IyTnNjRWRhUms1cVVqRktTRlJYZEhOaFZscDBaVWRHVjAxV2NGbFdNakZIWVd4S2MxZHVjRmROYWxaeVZtMTBVMU14VlhoVldHaFlZbGhTV0ZSV1pGTlZNVlp6V2taa2FWSnNjRlpWYlhoM1ZXMVdSMVpzWkZoaVJscFlXVlJHVDJSV1VuTlhiV2hFWWtaS1RGZFhNSGhUTVZsNVVtdG9VMkpIZHpGV2EyUXdZakpHYzFOWWJGSmlSMmh5Vld4b2QxWXlTa2RqUmxwWFlsaFNjbFJYY0Vkak1XUjFWR3hrVldKR2NIcFhWRTV2VVRGT1IyRkdXbWxXV0VKVVZGVmFkMlJzV25OalJFSmhWak5TY2xwR1RtcGxhelZIV2tab2ExWnNXbk5aTTJocllsWktiRlZzV21GV2JGcHlXVE5vYTAxV1NqVldSM040WXpKR2MxTnNXbXBpUjFFd1ZtMHhORmxYVGtkVWJGcE9ZVE5DVjFZd1ZrdFdWa3B6WTBac1dtSkdXbWhaYTFwM1YwWmFjMWt6YUZkTlJFWktWbTE0YTJJeVRuTlZiazVZWWxob1dGWnRjRWRaVjAxNFZHdGtXR0pyTlZsWmJHaFRWakZrU0ZOclpHcFNWR3hoV1ZSR2QxWXlSbk5YYkdSVFRWWndlbFl5ZEdGamJIQkhWR3hTVjJKSFVubFdiWGhUVjBac2NsVnNhRk5pVjJoWVdXdG9RMlJzV25STlNHUlhUV3R3V0ZVeWN6RldNa3BJVWxob1ZtSkhVbGxXYlRCNFRVWlZlVTFWWkZwaVIxSjZWbXBDWVZReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSMFZMVlRCYVlWSnJOVmRYYkZwRVlsVktXRlJYTlc5VWJGcFpZMGh3V2sxdWFISmFSVnBMWXpGa2MxcEZPV2hpUlhCUVZtdGFZVmRIVGtsaFJtUk9WbXh3ZVZkclVrZFpWbHBIVjJ4a1YwMHlhRmRaYkZKSFZqRmtjMXBHYUdoTk1VcFZWbGN4TkdReFpITlhhMlJxVFZkU1dGUlhjRXRTTVZaSFYyNU9XR0pIVWxsWlYzaExWMFpzY2xwR1pGaFNNMmhVV1Zod1YwNUdXWGxOVkZKV1RXNW9WVlp0TVRCWlZsbDRVMWhvV0dKck5WZFpWRXB2VmpKR2NsTnNXbXhWV0VKVVdXeGFTMVJHV25KWGJVWnJUVVJDTlZaSE1UQlpWbFY1Vkd0a1YySllVbGxXYlhoelZqSkZlRk5ZYUZCV00xSXpWbTF3UzFkR1duTlRiazVxVWxkNGFGVXdhRU5UUmxwMFlVWk9UbFp1UWpaV2JURTBZVEpOZUZkc1pHbFhSMmh5VldwR2QxSXhXblJTV0hCV1lrWktSVmxZY0VkVWJGcFpZVVpPVjAxV2NGbFdNV2h2VjBkS1dWVnJPVmRTVm5CWlZrWmtNRll3TVZkalJtaEVZa2Q0VjFSV1duZFhSbFowWlVoa1YwMVhVak5hVlZwV1pWWlNWMWRzWkdoTlZXdzFWVzAxUTFSV1duUmxSM